Skip to content

Commit

Permalink
Merge pull request #230 from shopware/fix-saas-issues-2
Browse files Browse the repository at this point in the history
fix: use correct system-config endpoint
  • Loading branch information
pweyck authored Dec 19, 2024
2 parents 49a585f + 52fc176 commit a2c18ca
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 100 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
on:
pull_request:
workflow_dispatch:

name: test
jobs:
eslint:
Expand Down Expand Up @@ -49,6 +51,7 @@ jobs:
APP_URL: http://localhost:8021
env:
APP_URL: ${{ matrix.instance.APP_URL }}
WEBSERVER_COMMAND: '' # this will disable the log output
steps:
- uses: actions/checkout@v4
- name: Compose up ${{ matrix.instance.compose-service }}
Expand All @@ -68,3 +71,22 @@ jobs:
name: playwright-report-${{ matrix.instance.compose-service }}
path: test-results/
retention-days: 7
test-saas:
timeout-minutes: 15
runs-on: ubuntu-latest
concurrency: saas # only allow one job at a time
env:
APP_URL: ${{ secrets.SAAS_APP_URL }}
SHOPWARE_ADMIN_USERNAME: ${{ secrets.SAAS_SHOPWARE_ADMIN_USERNAME }}
SHOPWARE_ADMIN_PASSWORD: ${{ secrets.SAAS_SHOPWARE_ADMIN_PASSWORD }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test --repeat-each 2
13 changes: 12 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,22 @@ if (process.env['ADMIN_URL']) {
process.env['ADMIN_URL'] = process.env['APP_URL'] + 'admin/';
}

if (!process.env['WEBSERVER_COMMAND']) {
if (process.env['WEBSERVER_COMMAND'] === defaultAppUrl) {
process.env['WEBSERVER_COMMAND'] = 'docker compose up --pull=always --quiet-pull shopware';
} else {
process.env['WEBSERVER_COMMAND'] = 'sleep 1h';
}
}

export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
timeout: 60000,
expect: {
timeout: 10_000,
},
retries: 0,
workers: process.env.CI ? 2 : 1,
reporter: process.env.CI ? [
Expand All @@ -33,7 +44,7 @@ export default defineConfig({
},
// We abuse this to wait for the external webserver
webServer: {
command: process.env['APP_URL'] === defaultAppUrl ? 'docker compose up --pull=always --quiet-pull shopware' : 'sleep 1h',
command: process.env['WEBSERVER_COMMAND'] ?? 'sleep 1h',
url: process.env['APP_URL'],
reuseExistingServer: true,
timeout: 180000,
Expand Down
82 changes: 38 additions & 44 deletions src/services/TestDataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import type {
CmsPage,
Country,
CustomerGroup,
SystemConfig,
SalesChannelAnalytics,
SalesChannelDomain,
Language,
Expand Down Expand Up @@ -110,6 +109,9 @@ export class TestDataService {
*/
private createdRecords: CreatedRecord[] = [];


private restoreSystemConfig: Record<string, unknown> = {};

/**
* A registry of all created sales channel records.
*
Expand Down Expand Up @@ -850,7 +852,7 @@ export class TestDataService {
* @param overrides - Specific data overrides that will be applied to the customer group data struct.
*/
async createCustomerGroup(overrides: Partial<CustomerGroup> = {}): Promise<CustomerGroup> {

const basicCustomerGroup = this.getBasicCustomerGroupStruct(overrides);

const response = await this.AdminApiClient.post('customer-group?_response=detail', {
Expand All @@ -866,37 +868,23 @@ export class TestDataService {
}

/**
* Creates a system config entry
* Set system config for default sales channel
*
* @param configurationKey - Config key for shop configurations.
* @param configurationValue - Config value as object for shop configurations (see {@link https://shopware.stoplight.io/docs/admin-api/9174d032146f8-create-a-new-system-config-resources|AdminApi Stoplight}).
* @param salesChannelId - Unique identity of sales channel.
* @param configs - Key value pairs to set
*/
async createSystemConfigEntry(
configurationKey: string,
configurationValue = {},
salesChannelId = '',
): Promise<SystemConfig> {

const systemConfigStruct = {
id: this.IdProvider.getIdPair().uuid,
configurationKey: configurationKey,
configurationValue: configurationValue,
salesChannelId: salesChannelId || null,
}

const response = await this.AdminApiClient.post('system-config?_response=detail', {
data: systemConfigStruct,
async setSystemConfig(configs: Record<string, unknown>): Promise<void> {
const response = await this.AdminApiClient.post(`_action/system-config?_response=detail&salesChannelId=${this.defaultSalesChannel.id}`, {
data: configs,
});
expect(response.ok()).toBeTruthy();

const { data: systemConfigEntry } = (await response.json()) as { data: SystemConfig };

this.addCreatedRecord('system_config', systemConfigEntry.id);
// we just unset all configs with this.defaultSalesChannel.id
// the defaults should take effect again
for (const key of Object.keys(configs)) {
this.restoreSystemConfig[key] = null;
}

await this.clearCaches();

return systemConfigEntry;
}

/**
Expand Down Expand Up @@ -930,8 +918,8 @@ export class TestDataService {
* @param overrides - Specific data overrides that will be applied to the custom field data struct.
*/
async createCustomField(
customFieldSetId: string,
overrides: Partial<CustomField> = {}
customFieldSetId: string,
overrides: Partial<CustomField> = {}
): Promise<CustomField> {
const customFieldStruct = this.getBasicCustomFieldStruct(overrides);

Expand All @@ -953,7 +941,7 @@ export class TestDataService {
* @param overrides - Specific data overrides that will be applied to the custom field set data struct.
*/
async createCustomFieldSet(
overrides: Partial<CustomFieldSet> = {}
overrides: Partial<CustomFieldSet> = {}
): Promise<CustomFieldSet> {
const customFieldSetStruct = this.getBasicCustomFieldSetStruct(overrides);

Expand All @@ -974,7 +962,7 @@ export class TestDataService {
*
* @param overrides - Specific data overrides that will be applied to the sales channel domain data struct.
*/
async createSalesChannelDomain(overrides: Partial<SalesChannelDomain> = {}): Promise<SalesChannelDomain> {
async createSalesChannelDomain(overrides: Partial<SalesChannelDomain> = {}): Promise<SalesChannelDomain> {
const salesChannelId = this.defaultSalesChannel.id;
const currencyId = this.defaultCurrencyId;
const languageId = this.defaultLanguageId;
Expand Down Expand Up @@ -1194,7 +1182,7 @@ export class TestDataService {

const { data: salesChannel } = await syncSalesChannelResponse.json();

this.addCreatedRecord('sales_channel_currency', {salesChannelId: salesChannelId, currencyId: currencyId})
this.addCreatedRecord('sales_channel_currency', { salesChannelId: salesChannelId, currencyId: currencyId })

return salesChannel;
}
Expand Down Expand Up @@ -1223,7 +1211,7 @@ export class TestDataService {
});
expect(syncSalesChannelResponse.ok()).toBeTruthy();

const { data: salesChannel } = (await syncSalesChannelResponse.json()) as { data: SalesChannel};
const { data: salesChannel } = (await syncSalesChannelResponse.json()) as { data: SalesChannel };

this.addCreatedSalesChannelRecord(salesChannelId, 'analyticsId');

Expand Down Expand Up @@ -1286,7 +1274,7 @@ export class TestDataService {

const { data: salesChannel } = await syncSalesChannelResponse.json();

this.addCreatedRecord('sales_channel_language', {salesChannelId: salesChannelId, languageId: languageId})
this.addCreatedRecord('sales_channel_language', { salesChannelId: salesChannelId, languageId: languageId })

return salesChannel;
}
Expand Down Expand Up @@ -1672,7 +1660,7 @@ export class TestDataService {
const deleteOperations: Record<string, SyncApiOperation> = {};
const priorityDeleteOperations: Record<string, SyncApiOperation> = {};

if(this.createdSalesChannelRecords) {
if (this.createdSalesChannelRecords) {
for (const salesChannelRecord of this.createdSalesChannelRecords) {
const salesChannelResponse = await this.AdminApiClient.patch(`sales-channel/${salesChannelRecord.salesChannelId}`, {
data: {
Expand Down Expand Up @@ -1713,7 +1701,13 @@ export class TestDataService {
data: priorityDeleteOperations,
});

return await this.AdminApiClient.post('_action/sync', {
await this.AdminApiClient.post(`_action/system-config?_response=detail&salesChannelId=${this.defaultSalesChannel.id}`, {
data: this.restoreSystemConfig,
});

await this.clearCaches();

return this.AdminApiClient.post('_action/sync', {
data: deleteOperations,
});
}
Expand Down Expand Up @@ -1787,9 +1781,9 @@ export class TestDataService {

const basicCountry = {
id: countryUuid,
name: 'Country-'+countryId,
iso: ''+countryId.substring(0,2),
iso3: ''+countryId.substring(0,3),
name: 'Country-' + countryId,
iso: '' + countryId.substring(0, 2),
iso3: '' + countryId.substring(0, 3),
active: true,
shippingAvailable: true,
};
Expand All @@ -1806,9 +1800,9 @@ export class TestDataService {

const basicCurrency = {
id: currencyUuid,
name: 'Currency-'+currencyId,
shortName: 'CUR'+currencyId,
isoCode: ''+currencyId.substring(0,3),
name: 'Currency-' + currencyId,
shortName: 'CUR' + currencyId,
isoCode: '' + currencyId.substring(0, 3),
symbol: 'C$',
factor: 2.40,
itemRounding: {
Expand Down Expand Up @@ -2488,7 +2482,7 @@ export class TestDataService {

getSalesChannelAnalyticsStruct(overrides: Partial<SalesChannelAnalytics> = {}): Partial<SalesChannelAnalytics> {
const salesChannelAnalyticsUuid = this.IdProvider.getIdPair().uuid;
const trackingId = this.IdProvider.getIdPair().id;
const trackingId = this.IdProvider.getIdPair().id;

const basicSalesChannelAnalyticsStruct = {
id: salesChannelAnalyticsUuid,
Expand Down Expand Up @@ -2517,7 +2511,7 @@ export class TestDataService {
},
},
position: 1,
relations: [
relations: [
{
id: this.IdProvider.getIdPair().uuid,
entityName: 'customer',
Expand Down Expand Up @@ -2559,7 +2553,7 @@ export class TestDataService {
currencyId: string,
languageId: string,
snippetSetId: string,
overrides:Partial<SalesChannelDomain> = {},
overrides: Partial<SalesChannelDomain> = {},
): Partial<SalesChannelDomain> {

const appUrl = process.env['APP_URL'];
Expand Down
2 changes: 0 additions & 2 deletions src/tasks/shop-admin-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { mergeTests } from '@playwright/test';
import { SaveProduct } from './shop-admin/Product/SaveProduct';
import { ExpectNotification } from './shop-admin/ExpectNotification';
import { CreateLinkTypeCategory } from './shop-admin/Category/CreateLinkTypeCategory';
import { SetSystemConfigValues } from './shop-admin/Settings/SetSystemConfigValues';
import { BulkEditProducts } from './shop-admin/Product/BulkEditProducts';
import { BulkEditCustomers } from './shop-admin/Customers/BulkEditCustomers';
import { AssignEntitiesToRule } from './shop-admin/Rule/AssignEntitiesToRule';
Expand All @@ -12,7 +11,6 @@ export const test = mergeTests(
SaveProduct,
ExpectNotification,
CreateLinkTypeCategory,
SetSystemConfigValues,
BulkEditProducts,
BulkEditCustomers,
AssignEntitiesToRule,
Expand Down
34 changes: 0 additions & 34 deletions src/tasks/shop-admin/Settings/SetSystemConfigValues.ts

This file was deleted.

2 changes: 1 addition & 1 deletion tests/PageObjects/AdministrationGeneral.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test('Administration page objects - General.', async ({
await ShopAdmin.expects(AdminManufacturerCreate.saveButton).toBeVisible();

await ShopAdmin.goesTo(AdminCustomerDetail.url(DefaultSalesChannel.customer.id));
await ShopAdmin.expects(AdminCustomerDetail.accountCard).toBeVisible();
await ShopAdmin.expects(AdminCustomerDetail.accountCard).toBeVisible({ timeout: 15_000 });

const category = await TestDataService.createCategory();
await ShopAdmin.goesTo(AdminCategories.url());
Expand Down
47 changes: 47 additions & 0 deletions tests/TestDataService/SystemConfig.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable playwright/no-conditional-expect */
import {
test,
expect,
APIResponse,
SystemConfig,
} from '../../src';

test('System config can be overwritten and is restored to defaults', async ({
AdminApiContext,
TestDataService,
DefaultSalesChannel,
}) => {
const fetchConfig = async (domain) => {
const response = await AdminApiContext.get(`_action/system-config?domain=${domain}&inherit=1&salesChannelId=${DefaultSalesChannel.salesChannel.id}`);
return (await response.json()) as { data: SystemConfig };
};
const defaults = await fetchConfig('core.register');
expect(defaults['core.register.minPasswordLength']).toBeDefined();

const overrides = {
'test.random.foo': true,
'core.register.minPasswordLength': 1337,
};

await TestDataService.setSystemConfig(overrides);

const c2 = await fetchConfig('core.register');
expect(c2['core.register.minPasswordLength']).toBeDefined();
expect(c2['core.register.minPasswordLength']).toStrictEqual(overrides['core.register.minPasswordLength']);

const c1 = await fetchConfig('test.random');
expect(c1['test.random.foo']).toBeDefined();
expect(c1['test.random.foo']).toStrictEqual(overrides['test.random.foo']);

// Test data clean-up with activated cleansing process
TestDataService.setCleanUp(true);
const cleanUpDeleteOperationsResponse = await TestDataService.cleanUp() as APIResponse;
expect(cleanUpDeleteOperationsResponse.ok()).toBeTruthy();

const afterCleanUpTest = await fetchConfig('test.random');
expect(afterCleanUpTest['test.random.foo']).not.toBeDefined();

const afterCleanUpCore = await fetchConfig('core.register');
expect(afterCleanUpCore['core.register.minPasswordLength']).toBeDefined();
expect(afterCleanUpCore['core.register.minPasswordLength']).toStrictEqual(defaults['core.register.minPasswordLength']);
});
Loading

0 comments on commit a2c18ca

Please sign in to comment.