@shopware-ag/acceptance-test-suite
Version:
Shopware Acceptance Test Suite
1,740 lines (1,698 loc) • 505 kB
JavaScript
import { test as test$e, expect, request, mergeTests } from '@playwright/test';
export { expect, mergeTests } from '@playwright/test';
import { AsyncLocalStorage } from 'async_hooks';
import { createInstance } from 'i18next';
import { Image, encode } from 'image-js';
import crypto from 'crypto';
import { stringify } from 'uuid';
import { satisfies } from 'compare-versions';
import fs from 'fs';
import { AxeBuilder } from '@axe-core/playwright';
import { createHtmlReport } from 'axe-html-reporter';
const test$d = test$e.extend({
SalesChannelBaseConfig: [
async ({ Country, Currency, Language, PaymentMethod, ShippingMethod, SnippetSet, Tax, Theme }, use) => {
await use({
currentLocaleId: Language.translationCode.id,
storefrontTypeId: "8a243080f92e4c719546314b577cf82b",
currentCurrencyId: Currency.id,
defaultCurrencyId: "b7d2554b0ce847cd82f3ac9bd1c0dfca",
currentLanguageId: Language.id,
defaultLanguageId: "2fbb5fe2e29a4d70aa5854ce7ce3e20b",
invoicePaymentMethodId: PaymentMethod.id,
defaultShippingMethod: ShippingMethod.id,
taxId: Tax.id,
currentCountryId: Country.id,
currentSnippetSetId: SnippetSet.id,
defaultThemeId: Theme.id,
appUrl: process.env["APP_URL"],
adminUrl: process.env["ADMIN_URL"] || `${process.env["APP_URL"]}admin/`
});
},
{ scope: "worker" }
],
DefaultSalesChannel: [
async ({ IdProvider, AdminApiContext, SalesChannelBaseConfig }, use) => {
const { id, uuid } = IdProvider.getWorkerDerivedStableId("salesChannel");
const { uuid: rootCategoryUuid } = IdProvider.getWorkerDerivedStableId("category");
const { uuid: customerGroupUuid } = IdProvider.getWorkerDerivedStableId("customerGroup");
const { uuid: domainUuid } = IdProvider.getWorkerDerivedStableId("domain");
const { uuid: customerUuid } = IdProvider.getWorkerDerivedStableId("customer");
const baseUrl = `${SalesChannelBaseConfig.appUrl}test-${uuid}/`;
await AdminApiContext.delete(`./customer/${customerUuid}`);
const wantedLanguages = /* @__PURE__ */ new Set([SalesChannelBaseConfig.currentLanguageId, SalesChannelBaseConfig.defaultLanguageId]);
const languages = [];
const result = await AdminApiContext.get(`./sales-channel/${uuid}/languages`);
if (result.ok()) {
const salesChannelLanguages = await result.json();
wantedLanguages.forEach((l) => {
if (!salesChannelLanguages.data.find((i) => i.id === l)) {
languages.push({ id: l });
}
});
} else {
wantedLanguages.forEach((l) => {
languages.push({ id: l });
});
}
const syncResp = await AdminApiContext.post("./_action/sync", {
data: {
"write-sales-channel": {
entity: "sales_channel",
action: "upsert",
payload: [
{
id: uuid,
name: `${id} acceptance test`,
typeId: SalesChannelBaseConfig.storefrontTypeId,
languageId: SalesChannelBaseConfig.currentLanguageId,
currencyId: SalesChannelBaseConfig.currentCurrencyId,
paymentMethodId: SalesChannelBaseConfig.invoicePaymentMethodId,
shippingMethodId: SalesChannelBaseConfig.defaultShippingMethod,
countryId: SalesChannelBaseConfig.currentCountryId,
accessKey: "SWSC" + uuid,
homeEnabled: true,
navigationCategory: {
id: rootCategoryUuid,
name: `${id} Acceptance test`,
displayNestedProducts: true,
type: "page",
productAssignmentType: "product"
},
domains: [
{
id: domainUuid,
url: baseUrl,
languageId: SalesChannelBaseConfig.currentLanguageId,
snippetSetId: SalesChannelBaseConfig.currentSnippetSetId,
currencyId: SalesChannelBaseConfig.currentCurrencyId
}
],
customerGroup: {
id: customerGroupUuid,
name: `${id} Acceptance test`
},
languages,
countries: [{ id: SalesChannelBaseConfig.currentCountryId }],
shippingMethods: [{ id: SalesChannelBaseConfig.defaultShippingMethod }],
paymentMethods: [{ id: SalesChannelBaseConfig.invoicePaymentMethodId }],
currencies: [{ id: SalesChannelBaseConfig.currentCurrencyId }]
}
]
}
}
});
expect(syncResp.ok()).toBeTruthy();
const salesChannelPromise = AdminApiContext.get(`./sales-channel/${uuid}`);
const salutationResponse = await AdminApiContext.get(`./salutation`);
const salutations = await salutationResponse.json();
const customerData = {
id: customerUuid,
email: `customer_${id}@example.com`,
password: "shopware",
salutationId: salutations.data[0].id,
languageId: SalesChannelBaseConfig.currentLanguageId,
defaultShippingAddress: {
firstName: `${id} admin`,
lastName: `${id} admin`,
city: "not",
street: "not",
zipcode: "not",
countryId: SalesChannelBaseConfig.currentCountryId,
salutationId: salutations.data[0].id
},
defaultBillingAddress: {
firstName: `${id} admin`,
lastName: `${id} admin`,
city: "not",
street: "not",
zipcode: "not",
countryId: SalesChannelBaseConfig.currentCountryId,
salutationId: salutations.data[0].id
},
firstName: `${id} admin`,
lastName: `${id} admin`,
salesChannelId: uuid,
groupId: customerGroupUuid,
customerNumber: `${customerUuid}`,
defaultPaymentMethodId: SalesChannelBaseConfig.invoicePaymentMethodId
};
const customerRespPromise = AdminApiContext.post("./customer?_response", {
data: customerData
});
const [customerResp, salesChannelResp] = await Promise.all([customerRespPromise, salesChannelPromise]);
expect(customerResp.ok()).toBeTruthy();
expect(salesChannelResp.ok()).toBeTruthy();
const customer = await customerResp.json();
const salesChannel = await salesChannelResp.json();
await use({
salesChannel: salesChannel.data,
customer: { ...customer.data, password: customerData.password },
url: baseUrl
});
},
{ scope: "worker" }
]
});
class AdminApiContext {
context;
options;
static defaultOptions = {
app_url: process.env["ADMIN_API_URL"] || process.env["APP_URL"] || "http://localhost:8000",
client_id: process.env["SHOPWARE_ACCESS_KEY_ID"] ?? "",
client_secret: process.env["SHOPWARE_SECRET_ACCESS_KEY"] ?? "",
admin_username: process.env["SHOPWARE_ADMIN_USERNAME"] || "admin",
admin_password: process.env["SHOPWARE_ADMIN_PASSWORD"] || "shopware",
ignoreHTTPSErrors: true,
authMaxRetries: 2,
authBaseDelayMs: 250,
access_token: ""
};
constructor(context, options) {
this.context = context;
this.options = options;
}
static async create(options) {
const contextOptions = {
...this.defaultOptions,
...options
};
const tmpContext = await this.createApiRequestContext(contextOptions);
if (!contextOptions.access_token) {
contextOptions.access_token = await this.authenticateWithRetry(tmpContext, contextOptions);
}
if (!contextOptions.client_id) {
contextOptions["access_token"] = await this.authenticateWithUserPassword(tmpContext, contextOptions);
const userContext = await this.createApiRequestContext(contextOptions);
const accessKeyData = await (await userContext.get("_action/access-key/intergration")).json();
const integrationData = {
admin: true,
label: "Playwright Acceptance Test Suite",
...accessKeyData
};
await userContext.post("integration", {
data: integrationData
});
contextOptions.client_id = accessKeyData.accessKey;
contextOptions.client_secret = accessKeyData.secretAccessKey;
}
contextOptions["access_token"] = await this.authenticateWithClientCredentials(tmpContext, contextOptions);
return new AdminApiContext(await this.createApiRequestContext(contextOptions), contextOptions);
}
static async createApiRequestContext(options) {
const extraHTTPHeaders = {
Accept: "application/json",
"Content-Type": "application/json"
};
if (options.access_token && options.access_token.length) {
extraHTTPHeaders["Authorization"] = "Bearer " + options.access_token;
}
return await request.newContext({
baseURL: `${options.app_url}api/`,
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
extraHTTPHeaders
});
}
static async authenticateWithRetry(tmpContext, options) {
const max = Math.max(0, options.authMaxRetries);
const baseDelay = Math.max(0, options.authBaseDelayMs);
let lastError;
for (let attempt = 0; attempt <= max; attempt++) {
try {
if (options.client_id && options.client_secret) {
const token = await this.authenticateWithClientCredentials(tmpContext, options);
if (token) return token;
}
if (options.admin_username && options.admin_password) {
const userToken = await this.authenticateWithUserPassword(tmpContext, options);
if (userToken) return userToken;
}
throw new Error("No valid authentication method available (missing credentials).");
} catch (err) {
lastError = err;
if (attempt === max) break;
const delay = baseDelay * Math.pow(2, attempt) + Math.floor(Math.random() * baseDelay);
await new Promise((r) => setTimeout(r, delay));
}
}
throw lastError instanceof Error ? lastError : new Error(`Authentication failed after ${max + 1} attempts: ${String(lastError)}`);
}
static async authenticateWithClientCredentials(context, options) {
const authResponse = await context.post("oauth/token", {
data: {
grant_type: "client_credentials",
client_id: options.client_id,
client_secret: options.client_secret,
scope: "write"
}
});
expect(authResponse.ok(), "Should authenticate with client credentials").toBeTruthy();
const authData = await authResponse.json();
if (!authData["access_token"]) {
throw new Error(`Failed to authenticate with client_id: ${options.client_id}`);
}
return authData["access_token"];
}
static async authenticateWithUserPassword(context, options) {
const authResponse = await context.post("oauth/token", {
data: {
client_id: "administration",
grant_type: "password",
username: options.admin_username,
password: options.admin_password,
scope: "write"
}
});
expect(authResponse.ok(), "Should authenticate with user credentials").toBeTruthy();
const authData = await authResponse.json();
if (!authData["access_token"]) {
throw new Error(`Failed to authenticate with user: ${options.admin_username}`);
}
return authData["access_token"];
}
async reauthenticate() {
const tmp = await AdminApiContext.createApiRequestContext({
...this.options,
access_token: ""
});
this.options.access_token = await AdminApiContext.authenticateWithRetry(tmp, this.options);
this.context = await AdminApiContext.createApiRequestContext(this.options);
}
isAuthenticated() {
return !!this.options["access_token"];
}
async refreshAccessToken() {
this.options["access_token"] = await AdminApiContext.authenticateWithClientCredentials(this.context, this.options);
this.context = await AdminApiContext.createApiRequestContext(this.options);
}
async get(url, options) {
return this.handleRequest("get", url, options);
}
async post(url, options) {
return this.handleRequest("post", url, options);
}
async patch(url, options) {
return this.handleRequest("patch", url, options);
}
async delete(url, options) {
return this.handleRequest("delete", url, options);
}
async fetch(url, options) {
return this.handleRequest("fetch", url, options);
}
async head(url, options) {
return this.handleRequest("head", url, options);
}
async handleRequest(method, url, options) {
const methodMap = {
get: this.context.get.bind(this.context),
post: this.context.post.bind(this.context),
patch: this.context.patch.bind(this.context),
delete: this.context.delete.bind(this.context),
fetch: this.context.fetch.bind(this.context),
head: this.context.head.bind(this.context)
};
const withAuth = {
...options,
data: options?.data ?? void 0,
headers: {
...options?.headers,
Authorization: `Bearer ${this.options.access_token}`
}
};
let response = await methodMap[method](url, withAuth);
if (response.status() === 401 || response.status() === 400) {
await this.reauthenticate();
const updatedOptions = {
...options,
data: options?.data ?? void 0,
headers: {
...options?.headers,
Authorization: `Bearer ${this.options["access_token"]}`
}
};
response = await methodMap[method](url, updatedOptions);
}
return response;
}
}
class StoreApiContext {
context;
options;
static defaultOptions = {
app_url: process.env["APP_URL"],
ignoreHTTPSErrors: true
};
constructor(context, options) {
this.context = context;
this.options = options;
}
static async create(options) {
const contextOptions = { ...this.defaultOptions, ...options };
return new StoreApiContext(await this.createContext(contextOptions), contextOptions);
}
static async createContext(options) {
const extraHTTPHeaders = {
Accept: "application/json",
"Content-Type": "application/json"
};
if (options["sw-access-key"]) {
extraHTTPHeaders["sw-access-key"] = options["sw-access-key"];
}
if (options["sw-context-token"]) {
extraHTTPHeaders["sw-context-token"] = options["sw-context-token"];
}
return await request.newContext({
baseURL: `${options["app_url"]}store-api/`,
ignoreHTTPSErrors: options.ignoreHTTPSErrors,
extraHTTPHeaders
});
}
async login(user) {
const loginResponse = await this.post(`account/login`, {
data: {
username: user.email,
password: user.password
}
});
const responseHeaders = loginResponse.headers();
if (!responseHeaders["sw-context-token"]) {
throw new Error(`Failed to login with user: ${user.email}`);
}
this.options["sw-context-token"] = responseHeaders["sw-context-token"];
this.context = await StoreApiContext.createContext(this.options);
return responseHeaders;
}
async get(url, options) {
return this.context.get(url, options);
}
async post(url, options) {
return this.context.post(url, options);
}
async patch(url, options) {
return this.context.patch(url, options);
}
async delete(url, options) {
return this.context.delete(url, options);
}
async fetch(url, options) {
return this.context.fetch(url, options);
}
async head(url, options) {
return this.context.head(url, options);
}
}
class MailpitApiContext {
context;
constructor(context) {
this.context = context;
}
/**
* Fetches email headers based on the recipient's email address.
* @param email - The email address of the recipient.
* @returns An Email object containing the email headers.
*/
async getEmailHeaders(email) {
const response = await this.context.get("/api/v1/search", {
params: {
kind: "To.Address",
query: email
}
});
const responseJson = await response.json();
const message = responseJson.messages[0];
return {
fromName: message.From.Name,
fromAddress: message.From.Address,
toName: message.To[0].Name,
toAddress: message.To[0].Address,
subject: message.Subject,
emailId: message.ID
};
}
/**
* Retrieves the body content of the email as an HTML string.
* @param email - The email address of the recipient.
* @returns A promise that resolves to the HTML content of the email.
*/
async getEmailBody(email) {
const emailId = (await this.getEmailHeaders(email)).emailId;
const response = await this.context.get(`view/${emailId}.html`);
const buffer = await response.body();
return buffer.toString("utf-8");
}
/**
* Generates the full email content, combining headers and body.
* @param email - The email address to fetch headers for.
* @returns A promise that resolves to the full email content as a string.
*/
async generateEmailContent(email) {
const headers = await this.getEmailHeaders(email);
const htmlTemplate = await this.getEmailBody(email);
const headerSection = `
<div style="font-family:arial; font-size:16px;" id="email-container">
<p id="from"><strong>From:</strong> ${headers.fromName} <${headers.fromAddress}></p>
<p id="to"><strong>To:</strong> ${headers.toName} <${headers.toAddress}></p>
<p id="subject"><strong>Subject:</strong> ${headers.subject}</p>
</div>
`;
return headerSection + htmlTemplate;
}
/**
* Retrieves the plain text content of the email.
* @param email - The email address of the recipient.
* @returns A promise that resolves to the plain text content of the email.
*/
async getRenderMessageTxt(email) {
const emailId = (await this.getEmailHeaders(email)).emailId;
const response = await this.context.get(`view/${emailId}.txt`);
const buffer = await response.body();
return buffer.toString("utf-8");
}
/**
* Extracts the first URL found in the plain text content of the latest email.
* @param email - The email address of the recipient.
* @returns A promise that resolves to the first URL found in the email content.
* @throws An error if no URL is found in the email content.
*/
async getLinkFromMail(email) {
const textContent = await this.getRenderMessageTxt(email);
const urlMatch = textContent.match(/https?:\/\/[^\s]+/);
if (urlMatch) {
return urlMatch[0];
}
throw new Error("No URL found in the email content");
}
/**
* Deletes a specific email by ID if provided, or deletes all emails if no ID is provided.
* @param emailId - The ID of the email to delete (optional).
*/
async deleteMail(emailId) {
const data = emailId ? { IDs: [emailId] } : {};
await this.context.delete(`api/v1/messages`, { data });
}
/**
* Creates a new MailpitApiContext instance with the appropriate configuration.
* @param baseURL - The base URL for the API.
* @returns A promise that resolves to a MailpitApiContext instance.
*/
static async create(baseURL) {
const extraHTTPHeaders = {
Accept: "application/json",
"Content-Type": "application/json"
};
const context = await request.newContext({
baseURL,
ignoreHTTPSErrors: true,
extraHTTPHeaders
});
return new MailpitApiContext(context);
}
}
const test$c = test$e.extend({
AdminApiContext: [
async ({}, use) => {
const adminApiContext = await AdminApiContext.create();
await use(adminApiContext);
},
{ scope: "worker" }
],
StoreApiContext: [
async ({ DefaultSalesChannel }, use) => {
const options = {
app_url: process.env["APP_URL"],
"sw-access-key": DefaultSalesChannel.salesChannel.accessKey,
ignoreHTTPSErrors: true
};
const storeApiContext = await StoreApiContext.create(options);
await use(storeApiContext);
},
{ scope: "worker" }
],
MailpitApiContext: [
async ({}, use) => {
const mailpitApiContext = await MailpitApiContext.create(process.env["MAILPIT_BASE_URL"]);
await use(mailpitApiContext);
},
{ scope: "worker" }
]
});
const isSaaSInstance = async (adminApiContext) => {
const instanceStatus = await adminApiContext.get("./instance/status");
return instanceStatus.ok();
};
const isThemeCompiled = async (context, storefrontUrl) => {
const response = await context.get(storefrontUrl);
const body = (await response.body()).toString();
const matches = body.match(/.*"(https?:\/\/.*all\.css[^"]*)".*/);
if (matches && matches?.length > 1) {
const allCssUrl = matches[1];
const allCssResponse = await context.get(allCssUrl);
return allCssResponse.status() < 400;
}
return false;
};
const clearDelayedCache = async (adminApiContext) => {
await adminApiContext.delete("./_action/cache-delayed?refreshOpenSearch=true");
};
async function mockApiCalls(page) {
await page.route(
"**/api/notification/message*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ notifications: [], timestamp: "2024-06-19 06:23:25.040" })
})
);
await page.route(
"**/api/_action/store/plugin/search*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ items: [], total: 0 })
})
);
await page.route(
"**/api/_action/store/updates*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ items: [], total: 0 })
})
);
await page.route(
"**/api/sbp/shop-info*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ items: [], total: 0 })
})
);
await page.route(
"**/api/sbp/shop-info*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: process.env.SBP_SHOP_INFO_JSON ?? "{}"
})
);
await page.route(
"**/api/sbp/bookableplans*",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: process.env.SBP_BOOKABLE_PLANS_JSON ?? "{}"
})
);
await page.route(
"**/api/sbp/nps/active-trigger",
(route) => route.fulfill({
status: 200,
contentType: "application/json",
body: '{"prompt":false,"trigger":["gone-live"]}'
})
);
}
const general$a = {
name: "Name",
active: "Active",
save: "Save",
cancel: "Cancel",
"delete": "Delete",
customFields: "Custom fields"
};
const actions$h = {
createCategory: "Create category",
addLandingPage: "Add landing page",
configureHomePage: "Configure home page",
newCategoryAfter: "New category after"
};
const tabs$b = {
general: "General",
products: "Products",
layout: "Layout",
seo: "SEO"
};
const fields$9 = {
categoryType: "Category type",
linkType: "Link type",
entity: "Entity",
openInNewTab: "Open in new tab"
};
const headings$5 = {
landingPages: "Landing pages"
};
const errors$5 = {
entityTypeNotFound: "Entity type not found"
};
const entities$1 = {
category: "Category",
product: "Product",
landingPage: "Landing page"
};
const administrationCategory = {
general: general$a,
actions: actions$h,
tabs: tabs$b,
fields: fields$9,
headings: headings$5,
errors: errors$5,
entities: entities$1
};
const listing$o = {
customers: "Customers",
bulkEdit: "Bulk edit",
"delete": "Delete",
cancel: "Cancel",
startBulkEdit: "Start bulk edit",
bulkEditItemsSelected: "Bulk edit - {{count}} items selected"
};
const detail$o = {
general: "General",
addresses: "Addresses",
orders: "Orders",
accept: "Accept",
decline: "Decline",
edit: "Edit",
customerGroupRequestMessage: "Access to customer group \"{{customerGroup}}\" requested.",
customerGroup: "Customer group",
accountStatus: "Account status",
language: "Language"
};
const bulkEdit$3 = {
headline: "Bulk edit: {{count}} customer",
applyChanges: "Apply changes",
success: "Bulk edit - Success",
close: "Close",
changes: {
customerGroup: "Change: Customer group",
accountStatus: "Change: Account status",
language: "Change: Language",
tags: "Change: Tags",
replyToRequest: "Reply to: Customer group request"
},
placeholders: {
selectCustomerGroup: "Select customer group...",
selectLanguage: "Select language...",
selectCustomerGroupRequestReply: "Select customer group request reply..."
}
};
const administrationCustomer = {
listing: listing$o,
detail: detail$o,
bulkEdit: bulkEdit$3
};
const common$b = {
technicalName: "Technical name",
position: "Position",
labelEnglishGB: "Label (English (GB))"
};
const listing$n = {
addNewSet: "Add new set"
};
const create$d = {
assignTo: "Assign To",
save: "Save",
cancel: "Cancel"
};
const detail$n = {
type: "Type",
modifiableViaStoreApi: "Modifiable via Store API",
placeholderEnglishGB: "Placeholder (English (GB))",
helpTextEnglishGB: "Help text (English (GB))",
availableInShoppingCart: "Available in shopping cart",
select: "Select...",
newCustomField: "New custom field",
editCustomField: "Edit custom field",
add: "Add"
};
const general$9 = {
customFields: "Custom fields"
};
const actions$g = {
edit: "Edit",
"delete": "Delete",
cancel: "Cancel",
applyChanges: "Apply changes"
};
const dialogs$7 = {
warning: "Warning",
deleteCustomField: "Delete custom field"
};
const administrationCustomField = {
common: common$b,
listing: listing$n,
create: create$d,
detail: detail$n,
general: general$9,
actions: actions$g,
dialogs: dialogs$7
};
const buttons$h = {
agree: "Agree",
disableDataSharing: "Disable data sharing",
allowAll: "Allow all",
rejectAll: "Reject All",
savePreferences: "Save Preferences",
declineUsageData: "Decline",
giveConsentUsageData: "Give Consent"
};
const checkboxes$3 = {
shareStoreData: "Share store data (anonymous)",
shareUsageData: "Share Usage data"
};
const headlines$3 = {
consentModal: "Help us to improve Shopware",
storeData: "Store data",
usageData: "Usage data",
dataConsent: "Data Consent"
};
const links$7 = {
storeDataCollectionDetails: "here",
privacyPolicy: "Privacy Policy"
};
const messages$7 = {
storeData: "Anonymous data from your Shopware environment such as orders, diagnostic data, and store data helps us to improve features. You can find an overview of all collected data and details of the agreement here.",
usageData: "We use personal usage data about how you interact with the administration to continously improve usability. You can find all details in our Privacy Policy.",
sharingData: "You are sharing data with us",
termsAgreement: "By clicking \"Agree\", you confirm that you are authorised to enter into this agreement on behalf of your company."
};
const administrationDataSharing = {
buttons: buttons$h,
checkboxes: checkboxes$3,
headlines: headlines$3,
links: links$7,
messages: messages$7
};
const listing$m = {
addDocument: "Add document"
};
const types$1 = {
invoice: "invoice"
};
const detail$m = {
displayDocumentInMyAccount: "Display document in \"My account\"",
displayDocumentInMyAccountSwitch: "Document in \"My account\"",
save: "Save"
};
const administrationDocument = {
listing: listing$m,
types: types$1,
detail: detail$m
};
const create$c = {
searchLayoutsPlaceholder: "Search layouts...",
assignLayout: "Assign layout",
createNewLayout: "Create new layout",
selectLayout: "Select layout"
};
const general$8 = {
name: "Name",
save: "Save",
active: "Active",
salesChannels: "Sales Channels",
seoUrl: "SEO URL"
};
const layout$1 = {
layout: "Layout",
changeLayout: "Change layout",
editInDesigner: "Edit in designer"
};
const administrationLandingPage = {
create: create$c,
general: general$8,
layout: layout$1
};
const listing$l = {
createNewLayout: "Create new layout"
};
const create$b = {
shopPage: "Shop page",
landingPage: "Landing page",
listingPage: "Listing page",
productPage: "Product page",
cancel: "Cancel",
save: "Save",
fullWidth: "Full width",
sidebar: "Sidebar",
back: "Back",
layoutName: "Layout name",
createLayout: "Create layout"
};
const detail$l = {
save: "Save",
settings: "Settings",
blocks: "Blocks",
navigator: "Navigator",
layoutAssignment: "Layout assignment"
};
const administrationLayout = {
listing: listing$l,
create: create$b,
detail: detail$l
};
const username$1 = "Username";
const emailAddress$3 = "Email address";
const password$3 = "Password";
const loginButton$3 = "Log in";
const administrationLogin = {
username: username$1,
emailAddress: emailAddress$3,
password: password$3,
loginButton: loginButton$3
};
const listing$k = {
"delete": "Delete",
downloadFlow: "Download flow",
testFlow: "Test flow",
flowTemplates: "Flow templates"
};
const create$a = {
addAction: "Add action",
addConditionIf: "Add condition (IF)",
addActionThen: "Add action (THEN)",
newFlow: "New flow",
name: "Name",
description: "Description",
priority: "Priority",
active: "Active",
tags: "Tags"
};
const detail$k = {
name: "Name"
};
const templates$1 = {
createFromTemplate: "Create new flow from template",
createNewFlowFromTemplate: "Create new flow from template"
};
const messages$6 = {
deleteConfirmation: "If you delete this flow, no more actions will be performed for the trigger. Are you sure you want to delete this flow?"
};
const administrationFlowBuilder = {
listing: listing$k,
create: create$a,
detail: detail$k,
templates: templates$1,
messages: messages$6
};
const dataSharing$1 = {
agree: "Agree",
notAtTheMoment: "Not at the moment",
acceptMessage: "Thank you for your participation!",
notAtTheMomentMessage: "You can at any time enter into the agreement and thus contribute to and profit from the constant evolution of our services",
agreementText: "By clicking \"Agree\", you confirm that you are authorised to enter into this agreement on behalf of your company."
};
const shopwareServices$1 = {
close: "Close",
exploreNow: "Explore now"
};
const userMenu$1 = {
logout: "Logout"
};
const order$1 = {
orderTitle: "Orders",
orderOverview: "Overview"
};
const administrationDashboard = {
dataSharing: dataSharing$1,
shopwareServices: shopwareServices$1,
userMenu: userMenu$1,
order: order$1
};
const listing$j = {
addManufacturer: "Add manufacturer"
};
const create$9 = {
name: "Name",
website: "Website",
save: "Save",
cancel: "Cancel"
};
const detail$j = {
customFields: "Custom fields"
};
const actions$f = {
edit: "Edit",
"delete": "Delete",
cancel: "Cancel"
};
const dialogs$6 = {
warning: "Warning"
};
const administrationManufacturer = {
listing: listing$j,
create: create$9,
detail: detail$j,
actions: actions$f,
dialogs: dialogs$6
};
const buttons$g = {
uploadFile: "Upload file",
addNewFolder: "Add new folder"
};
const search$2 = {
placeholder: "Search current folder..."
};
const actions$e = {
rename: "Rename",
"delete": "Delete"
};
const administrationMedia = {
buttons: buttons$g,
search: search$2,
actions: actions$e
};
const detail$i = {
items: "Items",
sendDocument: "Send document",
openActionsMenu: "Open actions menu"
};
const contextMenu$1 = {
sendDocument: "Send document",
openDocument: "Open document",
markAsSent: "Mark as sent"
};
const tabs$a = {
general: "General",
details: "Details",
documents: "Documents"
};
const listing$i = {
addOrder: "Add order"
};
const actions$d = {
view: "View",
"delete": "Delete",
cancel: "Cancel"
};
const dialogs$5 = {
warning: "Warning"
};
const administrationOrder = {
detail: detail$i,
contextMenu: contextMenu$1,
tabs: tabs$a,
listing: listing$i,
actions: actions$d,
dialogs: dialogs$5
};
const common$a = {
name: "Name",
availabilityRule: "Availability rule",
cashOnDelivery: "Cash on delivery",
paidInAdvance: "Paid in advance",
invoice: "Invoice"
};
const detail$h = {
};
const methods$5 = {
customPaymentMethod: "Custom payment method"
};
const administrationPayment = {
common: common$a,
detail: detail$h,
methods: methods$5
};
const tabs$9 = {
general: "General",
conditions: "Conditions",
discounts: "Discounts"
};
const cards$1 = {
preConditions: "Pre-conditions"
};
const headings$4 = {
promotionCodes: "Promotion codes"
};
const actions$c = {
addPromotion: "Add promotion",
addDiscount: "Add discount",
duplicate: "Duplicate",
"delete": "Delete",
edit: "Edit",
save: "Save",
cancel: "Cancel"
};
const fields$8 = {
priority: "Priority"
};
const sidebar$1 = {
refresh: "Refresh"
};
const administrationPromotion = {
tabs: tabs$9,
cards: cards$1,
headings: headings$4,
actions: actions$c,
fields: fields$8,
sidebar: sidebar$1
};
const buttons$f = {
createRule: "Create rule",
add: "Add",
addAssignment: "Add assignment",
save: "Save",
cancel: "Cancel"
};
const fields$7 = {
name: "Name",
priority: "Priority",
description: "Description"
};
const errors$4 = {
unknownRuleType: "Unknown rule type \"{ruleType}\". Valid rule types: \"{validTypes}\"."
};
const testData$1 = {
ruleDescription: "Rule description"
};
const text$3 = {
ruleNotInUse: "This rule is not in use",
filter: "Filter"
};
const administrationRule = {
buttons: buttons$f,
fields: fields$7,
errors: errors$4,
testData: testData$1,
text: text$3
};
const header$1 = {
settings: "Settings"
};
const links$6 = {
basicInformation: "Basic information",
cartSettings: "Cart settings",
languages: "Languages",
measurementSystem: "Measurement system",
numberRanges: "Number ranges",
productUnits: "Product units",
search: "Search",
statusManagement: "Status management",
customerGroups: "Customer groups",
loginAndSignUp: "Log-in & sign-up",
salutations: "Salutations",
flowBuilder: "Flow Builder",
importExport: "Import/Export",
ruleBuilder: "Rule builder",
countries: "Countries",
currencies: "Currencies",
snippets: "Snippets",
tax: "Tax",
customFields: "Custom fields",
emailTemplates: "Email templates",
media: "Media",
newsletter: "Newsletter",
seo: "SEO",
sitemap: "Sitemap",
tags: "Tags",
deliveryTimes: "Delivery times",
documents: "Documents",
essentialCharacteristics: "Essential characteristics",
paymentMethods: "Payment methods",
products: "Products",
shipping: "Shipping",
cachesAndIndexes: "Caches & indexes",
eventLogs: "Event logs",
firstRunWizard: "First Run Wizard",
integrations: "Integrations",
mailer: "Mailer",
messageQueueStatistics: "Message queue statistics",
privacy: "Privacy",
shopwareAccount: "Shopware Account",
shopwareServices: "Shopware Services",
shopwareUpdates: "Shopware updates",
storefront: "Storefront",
usersAndPermissions: "Users & permissions"
};
const administrationSettings = {
header: header$1,
links: links$6
};
const common$9 = {
name: "Name",
availabilityRule: "Availability rule"
};
const listing$h = {
addShippingMethod: "Add shipping method"
};
const detail$g = {
};
const methods$4 = {
standard: "Standard",
express: "Express",
customShippingMethod: "Custom shipping method"
};
const dialogs$4 = {
warning: "Warning",
cancel: "Cancel",
"delete": "Delete"
};
const administrationShipping = {
common: common$9,
listing: listing$h,
detail: detail$g,
methods: methods$4,
dialogs: dialogs$4
};
const tabs$8 = {
searchPreferences: "Search preferences",
privacyPreferences: "Privacy preferences"
};
const fields$6 = {
firstName: "First name",
lastName: "Last name",
username: "Username",
email: "Email"
};
const buttons$e = {
deselectAll: "Deselect all"
};
const links$5 = {
privacy: "Privacy Policy"
};
const checkboxes$2 = {
usageDataCheckbox: "Share Usage Data"
};
const headlines$2 = {
usageDataHeadline: "Usage data",
cardTitle: "Help us to improve Shopware"
};
const administrationYourProfile = {
tabs: tabs$8,
fields: fields$6,
buttons: buttons$e,
links: links$5,
checkboxes: checkboxes$2,
headlines: headlines$2
};
const listing$g = {
customerGroups: "Customer groups"
};
const detail$f = {
technicalUrl: "Technical URL"
};
const create$8 = {
title: "New customer group",
cardTitle: "Customer group",
save: "Save",
cancel: "Cancel",
customSignupForm: "Custom signup form",
companySignupForm: "Company signup form",
seoMetaDescription: "SEO meta description"
};
const administrationCustomerGroup = {
listing: listing$g,
detail: detail$f,
create: create$8
};
const welcome$1 = {
text: "Welcome to the Shopware 6 Administration"
};
const buttons$d = {
install: "Install",
installMigrationAssistant: "Install Migration Assistant",
installDemoData: "Install demo data",
next: "Next",
skip: "Skip",
finish: "Finish",
back: "Back",
configureLater: "Configure later",
configureOwnSmtpServer: "Configure own SMTP server"
};
const headers$1 = {
dataImport: "Getting started with Shopware 6",
defaultValues: "Setup default values",
mailerConfiguration: "Mailer configuration",
payPalSetup: "Setup PayPal",
extensions: "Extensions",
shopwareAccount: "Shopware Account",
shopwareStore: "Shopware Store",
done: "Done"
};
const fields$5 = {
host: "Host",
port: "Port",
username: "Username",
password: "Password",
senderAddress: "Sender address",
deliveryAddress: "Delivery address",
disableEmailDelivery: "Disable email delivery"
};
const placeholders$1 = {
selectSalesChannels: "Select Sales Channels...",
enterEmailAddress: "Enter your email address...",
enterPassword: "Enter your password..."
};
const regions$1 = {
germanRegion: "Germany / Austria / Switzerland",
tools: "Tools"
};
const text$2 = {
smtpServer: "SMTP server",
globalRecommendations: "Global recommendations",
allDone: "All done!",
forgotPassword: "Did you forget your password?"
};
const administrationFirstRunWizard = {
welcome: welcome$1,
buttons: buttons$d,
headers: headers$1,
fields: fields$5,
placeholders: placeholders$1,
regions: regions$1,
text: text$2
};
const headings$3 = {
futureProofStore: "Future proof your store with Shopware Services"
};
const buttons$c = {
activateServices: "Activate Services",
grantPermissions: "Grant permissions",
deactivate: "Deactivate",
exploreNow: "Explore now"
};
const modals$3 = {
deactivateServices: "Deactivate Shopware Services"
};
const messages$5 = {
accessDenied: "Access denied"
};
const links$4 = {
shopwareServices: "Shopware Services"
};
const dashboard$1 = {
shopwareServicesIntroduction: "Introducing Shopware Services"
};
const administrationShopwareServices = {
headings: headings$3,
buttons: buttons$c,
modals: modals$3,
messages: messages$5,
links: links$4,
dashboard: dashboard$1
};
const detail$e = {
customFields: "Custom fields",
colorProperty: "Color",
sizeProperty: "Size",
stockPlaceholder: "Enter quantity in stock...",
restockTimePlaceholder: "Enter restock time in days..."
};
const variants$1 = {
properties: "Properties"
};
const flows$1 = {
tag: "Tag:"
};
const errors$3 = {
followingErrorOccurred: "The following error occurred:"
};
const bulkEdit$2 = {
applyChanges: "Apply changes",
success: "Bulk edit - Success",
close: "Close"
};
const listing$f = {
bulkEdit: "Bulk edit",
startBulkEdit: "Start bulk edit"
};
const tabs$7 = {
specifications: "Specifications",
advancedPricing: "Advanced pricing",
variants: "Variants",
layout: "Layout",
crossSelling: "Cross Selling",
seo: "SEO",
reviews: "Reviews"
};
const buttons$b = {
save: "Save",
uploadFile: "Upload file",
generateVariants: "Generate variants",
next: "Next",
saveVariants: "Save variants"
};
const modals$2 = {
generateVariants: "Generate variants"
};
const propertyValues$1 = {
blue: "Blue",
red: "Red",
green: "Green",
small: "Small",
medium: "Medium",
large: "Large"
};
const administrationProduct = {
detail: detail$e,
variants: variants$1,
flows: flows$1,
errors: errors$3,
bulkEdit: bulkEdit$2,
listing: listing$f,
tabs: tabs$7,
buttons: buttons$b,
modals: modals$2,
propertyValues: propertyValues$1
};
const tabs$6 = {
general: "General",
products: "Products",
theme: "Theme",
analytics: "Analytics"
};
const buttons$a = {
addDomain: "Add domain"
};
const administrationSalesChannel = {
tabs: tabs$6,
buttons: buttons$a
};
const common$8 = {
salutation: "Salutation",
firstName: "First name",
lastName: "Last name",
company: "Company",
department: "Department",
back: "Back",
changePassword: "Change password",
passwordUpdated: "Your password has been updated.",
cashOnDelivery: "Cash on delivery",
paidInAdvance: "Paid in advance",
invoice: "Invoice"
};
const login$1 = {
email: "Your email address",
password: "Your password",
logIn: "Log in",
logOut: "Log out",
forgotPassword: "I have forgotten my password.",
invalidCredentials: "Could not find an account that matches the given credentials.",
successfulLogout: "Successfully logged out."
};
const profile$1 = {
saveChanges: "Save changes",
changeEmail: "Change email address",
emailConfirmation: "Email address confirmation",
emailUpdated: "Your email address has been updated.",
invalidEmail: "Invalid email address.",
emailUpdateFailure: "Email address could not be changed.",
passwordTooShort: "Input is too short.",
passwordUpdateFailure: "Password could not be changed."
};
const orders$1 = {
download: "Download",
cancelOrder: "Cancel order",
repeatOrder: "Repeat order",
changePaymentMethod: "Change payment method",
actions: "Actions",
expand: "Expand",
showDetails: "Show details",
creditItem: "CreditItem",
orderNumber: "Order number",
plusVat: "plus",
includeVat: "incl.",
vatSuffix: "% VAT",
shippingCosts: "Shipping costs:",
totalGross: "Total (gross):",
headlineCompletePayment: "Complete payment",
buttonCompletePayment: "Complete payment",
editCompleted: "Thank you for updating your order!"
};
const addresses$1 = {
street: "Street",
streetAddress: "Street address",
city: "City",
country: "Country",
postalCode: "Postal code",
state: "State",
editAddress: "Edit address",
addNewAddress: "Add new address",
useAsDefaultBilling: "Use as default billing address",
useAsDefaultShipping: "Use as default shipping address",
deliveryNotPossible: "A delivery to this country is not possible."
};
const registration$1 = {
"continue": "Continue",
differentShippingAddress: "Shipping and billing address do not match."
};
const general$7 = {
overview: "Overview",
personalData: "Personal data",
defaultPaymentMethod: "Default payment method",
defaultBillingAddress: "Default billing address",
defaultShippingAddress: "Default shipping address",
yourAccount: "Your account",
newsletter: "Yes, I would like to",
newsletterRegistrationSuccess: "You have successfully subscribed to the newsletter.",
cannotDeliverToCountry: "We can not deliver to the country that is stored in your delivery address.",
shippingNotPossible: "Shipping to the selected shipping address is currently not possible.",
customerGroupAccessRequested: "Access to customer group \"{{customerGroup}}\" requested."
};
const navigation$1 = {
overview: "Overview",
yourProfile: "Your profile",
addresses: "Addresses",
orders: "Orders",
logout: "Log out"
};
const recovery$1 = {
title: "Password recovery",
subtitle: "We will send you a confirmation email. Click the link in that email in order to change your password.",
requestEmail: "Request email",
emailSent: "If the provided email address is registered, a confirmation email including a password reset link has been sent.",
invalidLink: "The password reset link seems to be invalid.",
newPassword: "New password",
passwordConfirmation: "Password confirmation"
};
const payment$1 = {
change: "Change"
};
const storefrontAccount = {
common: common$8,
login: login$1,
profile: profile$1,
orders: orders$1,
addresses: addresses$1,
registration: registration$1,
general: general$7,
navigation: navigation$1,
recovery: recovery$1,
payment: payment$1
};
const common$7 = {
salutation: "Salutation",
firstName: "First name",
lastName: "Last name",
company: "Company",
department: "Department",
street: "Street",
postalCode: "Postal code"
};
const actions$b = {
editAddress: "Edit",
useAsDefaultBilling: "Use as default billing address",
useAsDefaultShipping: "Use as default shipping address",
addressOptions: "Address options",
deleteAddress: "Delete address"
};
const badges$1 = {
defaultBilling: "Default billing address",
defaultShipping: "Default shipping address"
};
const messages$4 = {
deliveryNotPossible: "A delivery to this country is not possible."
};
const storefrontAddress = {
common: common$7,
actions: actions$b,
badges: badges$1,
messages: messages$4
};
const common$6 = {
back: "Back",
paymentMethod: "Payment method",
cashOnDelivery: "Cash on delivery",
paidInAdvance: "Paid in advance",
invoice: "Invoice",
shippingMethod: "Shipping method",
standard: "Standard",
express: "Express",
grandTotal: "Grand total",
plusVat: "plus",
vatSuffix: "% VAT"
};
const cart$1 = {
shoppingCart: "Shopping cart",
goToCheckout: "Go to checkout",
displayShoppingCart: "Display shopping cart",
continueShopping: "Continue shopping",
promoCode: "Promo code",
emptyCart: "Your shopping cart is empty.",
quantity: "Quantity",
stockReached: "only available 1 times"
};
const confirm$1 = {
completeOrder: "Complete order",
submitOrder: "Submit order",
termsAndConditions: "I have read and accepted the general terms and conditions.",
immediateAccessToDigitalProduct: "I want immediate access to the digital content and I acknowledge that thereby I waive my right to cancel."
};
const finish$1 = {
thankYouForOrder: "Thank you for your order"
};
const orderEdit$1 = {
cancelOrder: "Cancel order"
};
const storefrontCheckout = {
common: common$6,
cart: cart$1,
confirm: confirm$1,
finish: finish$1,
orderEdit: orderEdit$1
};
const addToCart$1 = "Add to shopping cart";
const quantity$2 = "Quantity";
const addToWishlist$1 = "Add to wishlist";
const removeFromWishlist$1 = "Remove from wishlist";
const deliveryTime$1 = "Ready to ship today,\\nDelivery time appr. 1-3 workdays";
const review$1 = {
tabTitle: "Reviews",
title: "Title",
text: "Your review",
emptyText: "No reviews found. Share your insights with others.",
submitMessage: "Thank you for submitting your review. We will examine the review and eventually unlock it, please be patient."
};
const listing$e = {
sorting: "Sorting",
noProductsFound: "No products found."
};
const storefrontProduct = {
addToCart: addToCart$1,
quantity: quantity$2,
addToWishlist: addToWishlist$1,
removeFromWishlist: removeFromWishlist$1,
deliveryTime: deliveryTime$1,
review: review$1,
listing: listing$e
};
const pageNotFound$1 = {
title: "Page not found",
backToShop: "Back to shop"
};
const home$1 = {
yourAccount: "Your account",
manufacturerFilter: "Manufacturer",
priceFilter: "Price",
resetAll: "Reset all",
freeShipping: "Free shipping"
};
const footer$1 = {
contactForm: "contact form"
};
const category$1 = {
sorting: "Sorting",
addToCart: "Add to shopping cart",
noProductsFound: "No products found."
};
const storefrontNavigation = {
pageNotFound: pageNotFound$1,
home: home$1,
footer: footer$1,
category: category$1
};
const title$4 = "Contact";
const link$1 = {
contactForm: "Contact form"
};
const form$1 = {
contact: "Contact",
salutation: "Salutation",
firstName: "First name",
lastName: "Last name",
emailAddress: "Your email address",
phone: "Phone",
subject: "Subject",
comment: "Comment",
submit: "Submit",
privacyPolicy: "By selecting continue you confirm that you have read and agree to our"
};
const email$1 = {
from: "doNotReply@localhost.com",
subject: "Your sign-up"
};
const storefrontContact = {
title: title$4,
link: link$1,
form: form$1,
email: email$1
};
const cookie$1 = {
onlyTechnicallyRequired: "Only technically required",
configure: "Configure",
acceptAllCookies: "Accept all cookies",
preferences: "Cookie preferences",
marketing: "Marketing"
};
const storefrontConsent = {
cookie: cookie$1
};
const topBarNav$1 = "Shop settings";
const currencyDropdown$1 = "Change currency";
const languageDropdown$1 = "Change language";
const skipToContentLink$1 = "Skip to main content";
const searchInputAriaLabel$1 = "Enter search term...";
const wishlistIcon$1 = "Wishlist";
const shoppingCart$1 = "Shopping cart";
const storefrontHeader = {
topBarNav: topBarNav$1,
currencyDropdown: currencyDropdown$1,
languageDropdown: languageDropdown$1,
skipToContentLink: skipToContentLink$1,
searchInputAriaLabel: searchInputAriaLabel$1,
wishlistIcon: wishlistIcon$1,
shoppingCart: shoppingCart$1
};
const account$1 = {
yourAccount: "Your account"
};
const consent$1 = {
close: "Close cookie preferences",
onlyTechnicallyRequired: "Only technically required",
technicallyRequired: "Technically required",
configure: "Configure",
acceptAllCookies: "Accept all cookies",
cookiePreferences: "Cookie preferences",
marketing: "Marketing",
statistics: "Statistics",
save: "Save"
};
const filters$1 = {
filterPanel: "Filter products",
labelPrefix: "Filter by ",
manufacturer: "Manufacturer",
price: "Price",
resetAll: "Reset all",
freeShipping: "Add filter: Free shipping",
rating: "minimum rating",
minRating: "min. {{rating}}/5"
};
const listing$d = {
addToShoppingCart: "Add to shopping cart"
};
const storefrontHome = {
account: account$1,
consent: consent$1,
filters: filters$1,
listing: listing$d
};
const emailAddress$2 = "Your email address";
const password$2 = "Your password";
const loginButton$2 = "Log in";
const forgotPassword$1 = "I have forgotten my password.";
const logout$1 = "Log out";
const invalidCredentials$1 = "Could not find an account that matches the given credentials.";
const successfulLogout$1 = "Successfully logged out.";
const passwordUpdated$1 = "Your password has been updated.";
const register$1 = {
salutation: "Salutat