Skip to content

Commit

Permalink
Refactoring of browser test.
Browse files Browse the repository at this point in the history
  • Loading branch information
dmiseev committed Jan 20, 2025
1 parent a317ee8 commit a5f473b
Show file tree
Hide file tree
Showing 12 changed files with 183 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module.exports = {
indent: ['error', 2, { SwitchCase: 1 }],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
'function-paren-newline': ['error', 'multiline'],
'function-paren-newline': ['off'],
'max-len': [
'error',
{
Expand Down
7 changes: 6 additions & 1 deletion dashboards/k6-load-testing-results.json
Original file line number Diff line number Diff line change
Expand Up @@ -1654,9 +1654,14 @@
"selected": false,
"text": "S2_get_search",
"value": "S2_get_search"
},
{
"selected": false,
"text": "M8_view_dashboard",
"value": "M8_view_dashboard"
}
],
"query": "http_req_duration,http_req_blocked,http_req_connecting,http_req_looking_up,http_req_receiving,http_req_sending,http_req_waiting,SAPI7_post_checkout,SAPI9_post_checkout,SAPI15_post_cart_reorder,SAPI19_post_cart_reorder,SAPI16_post_cart_reorder,SAPI17_delete_carts,SAPI18_post_checkout,SAPI20_post_cart_reorder,SAPI21_delete_carts,SAPI22_post_checkout,S2_get_search",
"query": "http_req_duration,http_req_blocked,http_req_connecting,http_req_looking_up,http_req_receiving,http_req_sending,http_req_waiting,SAPI7_post_checkout,SAPI9_post_checkout,SAPI15_post_cart_reorder,SAPI19_post_cart_reorder,SAPI16_post_cart_reorder,SAPI17_delete_carts,SAPI18_post_checkout,SAPI20_post_cart_reorder,SAPI21_delete_carts,SAPI22_post_checkout,S2_get_search,M8_view_dashboard",
"type": "custom"
}
]
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@
"license": "proprietary",
"main": "index.js",
"scripts": {
"build:one": "webpack --env entryPattern=./src/tests/dashboard/M8_view_dashboard.test.js",
"build:one": "webpack --env entryPattern=./src/tests/product-search/S2_product_search_1.test.js",
"build:all": "webpack --env entryPattern=./src/tests/**/*.test.js",
"build:S": "webpack --env entryPattern=./src/tests/**/S*.test.js",
"build:M": "webpack --env entryPattern=./src/tests/**/M*.test.js",
"build:SAPI": "webpack --env entryPattern=./src/tests/**/SAPI*.test.js",
"build:checkout": "webpack --env entryPattern=./src/tests/checkout/*.test.js",
"build:cart-reorder": "webpack --env entryPattern=./src/tests/cart-reorder/*.test.js",
"build:order-amendment": "webpack --env entryPattern=./src/tests/order-amendment/*.test.js",
"build:product-search": "webpack --env entryPattern=./src/tests/product-search/*.test.js",
"build:dashboard": "webpack --env entryPattern=./src/tests/dashboard/*.test.js",
"docker:suite:up": "docker-compose -f docker-compose.suite.local.yml up -d influxdb grafana",
"docker:suite:down": "docker-compose -f docker-compose.suite.local.yml down -v --remove-orphans",
"docker:suite:run": "docker-compose -f docker-compose.suite.local.yml run --rm k6 run /$(find dist -name '*.test.js' -print0 | xargs -0)",
"prettier:check": "prettier . --check",
"prettier:write": "prettier . --write"
"prettier:write": "prettier . --write",
"lint": "npx eslint ./src"
},
"devDependencies": {
"@babel/core": "^7.22.0",
Expand Down
24 changes: 24 additions & 0 deletions src/fixtures/abstract.fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,28 @@ export class AbstractFixture {

return res;
}

static runConsoleCommands(commands) {
const operations = commands.map((command) => {
return {
type: 'cli-command',
name: command,
};
});

const payload = {
data: {
type: 'dynamic-fixtures',
attributes: {
operations: operations,
},
},
};

return http.post(http.url`${EnvironmentUtil.getBackendApiUrl()}/dynamic-fixtures`, payload, {
headers: {
'Content-Type': 'application/vnd.api+json',
},
});
}
}
42 changes: 22 additions & 20 deletions src/fixtures/merchant-user.fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ export class MerchantUserFixture extends AbstractFixture {
const responseData = JSON.parse(response.body).data;

return responseData
.filter((item) => /^merchantUser\d+$/.test(item.attributes.key))
.map((item) => {
const { id_user, username, first_name, last_name, status } = item.attributes.data;
.filter((item) => /^merchantUser\d+$/.test(item.attributes.key))
.map((item) => {
const { id_user, username, first_name, last_name, status } = item.attributes.data;

return {
id: id_user,
username,
password: DEFAULT_PASSWORD,
firstName: first_name,
lastName: last_name,
status,
};
});
return {
id: id_user,
username,
password: DEFAULT_PASSWORD,
firstName: first_name,
lastName: last_name,
status,
};
});
}

static iterateData(data, vus = __VU) {
Expand All @@ -47,7 +47,9 @@ export class MerchantUserFixture extends AbstractFixture {
},
];

const merchantUsers = Array.from({ length: this.merchantUserCount }, (_, i) => this._createMerchantPayload(i)).flat();
const merchantUsers = Array.from({ length: this.merchantUserCount }, (_, i) =>
this._createMerchantPayload(i)
).flat();

return JSON.stringify({
data: {
Expand All @@ -64,16 +66,16 @@ export class MerchantUserFixture extends AbstractFixture {
const merchantUserKey = `merchantUser${index + 1}`;
return [
{
type: "helper",
name: "haveUser",
type: 'helper',
name: 'haveUser',
key: merchantUserKey,
arguments: [{ "password": DEFAULT_PASSWORD }]
arguments: [{ password: DEFAULT_PASSWORD }],
},
{
type: "helper",
name: "haveMerchantUserWithAclEntities",
"arguments": ["#merchant", `#${merchantUserKey}`]
type: 'helper',
name: 'haveMerchantUserWithAclEntities',
arguments: ['#merchant', `#${merchantUserKey}`],
},
];
}
}
}
36 changes: 36 additions & 0 deletions src/pages/mp/dashboard.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import EnvironmentUtil from '../../utils/environment.util';
import { check } from 'k6';

export class DashboardPage {
constructor(page) {
this.page = page;
this.header = page.locator('h1');
}

async navigate() {
await this.page.goto(`${EnvironmentUtil.getMerchantPortalUrl()}/dashboard-merchant-portal-gui/dashboard`);
await this.page.waitForLoadState('load');
}

async verifyHeader() {
await this.header.waitFor();
const headerText = await this.header.textContent();

check(headerText, {
'Dashboard page was loaded': (text) => text === 'Dashboard',
});
}

async getDurationTime() {
await this.page.evaluate(() => window.performance.mark('page-visit'));
const marks = await this.page.evaluate(() =>
JSON.parse(JSON.stringify(window.performance.getEntriesByType('mark')))
);

if (marks.length > 0) {
return marks[0].startTime;
}

throw new Error('No marks found');
}
}
30 changes: 30 additions & 0 deletions src/pages/mp/login.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import EnvironmentUtil from '../../utils/environment.util';
import { check } from 'k6';

export class LoginPage {
constructor(page) {
this.page = page;
this.usernameInput = page.locator('#security-merchant-portal-gui_username');
this.passwordInput = page.locator('#security-merchant-portal-gui_password');
this.submitButton = page.locator('[name="security-merchant-portal-gui"] button[type="submit"]');
this.header = page.locator('h1');
}

async navigate() {
await this.page.goto(`${EnvironmentUtil.getMerchantPortalUrl()}/security-merchant-portal-gui/login`);
}

async login(username, password) {
await this.usernameInput.type(username);
await this.passwordInput.type(password);

await Promise.all([this.page.waitForNavigation(), this.submitButton.click()]);

await this.header.waitFor();
const headerText = await this.header.textContent();

check(headerText, {
'Login was successful': (text) => text === 'Dashboard',
});
}
}
4 changes: 2 additions & 2 deletions src/pages/catalog.page.js → src/pages/yves/catalog.page.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import AbstractPage from './abstract.page';
import EnvironmentUtil from '../utils/environment.util';
import http from 'k6/http';
import AbstractPage from '../abstract.page';
import EnvironmentUtil from '../../utils/environment.util';

export default class CatalogPage extends AbstractPage {
search(query) {
Expand Down
134 changes: 54 additions & 80 deletions src/tests/dashboard/M8_view_dashboard.test.js
Original file line number Diff line number Diff line change
@@ -1,104 +1,78 @@
import {group} from 'k6';
import OptionsUtil from '../../utils/options.util';
import {createMetrics} from '../../utils/metric.util';
import {check} from 'k6';
import { createMetrics } from '../../utils/metric.util';
import EnvironmentUtil from '../../utils/environment.util';
import {MerchantUserFixture} from "../../fixtures/merchant-user.fixture";
import {browser} from 'k6/browser';
import { MerchantUserFixture } from '../../fixtures/merchant-user.fixture';
import { browser } from 'k6/browser';
import { DashboardPage } from '../../pages/mp/dashboard.page';
import { LoginPage } from '../../pages/mp/login.page';

const testConfiguration = {
...EnvironmentUtil.getDefaultTestConfiguration(),
id: 'M8',
group: 'Dashboard',
vus: 1,
iterations: 10,
metrics: ['M8_view_dashboard'],
thresholds: {
M8_view_dashboard: {
smoke: ['avg<475'],
load: ['avg<475'],
},
...EnvironmentUtil.getDefaultTestConfiguration(),
id: 'M8',
group: 'Dashboard',
metrics: ['M8_view_dashboard'],
thresholds: {
M8_view_dashboard: {
smoke: ['avg<475'],
load: ['avg<475'],
},
},
};

const {metrics, metricThresholds} = createMetrics(testConfiguration);
const { metrics, metricThresholds } = createMetrics(testConfiguration);
export const options = OptionsUtil.loadOptions(testConfiguration, metricThresholds);

export function setup() {
const dynamicFixture = new MerchantUserFixture({
idMerchant: 1,
merchantUserCount: testConfiguration.vus,
});
const dynamicFixture = new MerchantUserFixture({
idMerchant: 1,
merchantUserCount: testConfiguration.vus,
});

return dynamicFixture.getData();
return dynamicFixture.getData();
}

async function login(merchantUser) {
const browserContext = await browser.newContext(); // Create browser context
const page = await browserContext.newPage(); // Create a new page in the context

try {
// Navigate to login page
await page.goto(`${EnvironmentUtil.getMerchantPortalUrl()}/security-merchant-portal-gui/login`);

// Fill in login credentials
await page.locator('#security-merchant-portal-gui_username').type(merchantUser.username);
await page.locator('#security-merchant-portal-gui_password').type(merchantUser.password);

// Submit the form
const submitButton = page.locator('[name="security-merchant-portal-gui"] button[type="submit"]');
await Promise.all([
page.waitForNavigation(), // Wait for navigation to complete
submitButton.click(), // Click the submit button
]);
export function teardown() {
MerchantUserFixture.runConsoleCommands(['console queue:worker:start --stop-when-empty']);
}

// Ensure the header is loaded
const header = page.locator('h1');
await header.waitFor(); // Wait for the header to appear
export default async function (data) {
const merchantUser = MerchantUserFixture.iterateData(data);
let browserContext = await browser.newContext();

// Validate the header text
const headerText = await header.textContent();
check(headerText, {
'Header text is correct': (text) => text === 'Dashboard',
});
try {
browserContext = await login(browserContext, merchantUser);
const durationTime = await openDashboardPage(browserContext);

return browserContext; // Return the browser context for reuse
} catch (error) {
console.error('Error during login:', error);
throw error; // Rethrow error for debugging
} finally {
await page.close(); // Always close the page
}
metrics[testConfiguration.metrics[0]].add(durationTime);
} finally {
await browserContext.close();
}
}

export default async function (data) {
const merchantUser = MerchantUserFixture.iterateData(data);
const context = await login(merchantUser);

group(testConfiguration.group, async () => {
const page = await context.newPage();
async function login(browserContext, merchantUser) {
const page = await browserContext.newPage({ headless: false });
const loginPage = new LoginPage(page);

try {
await page.goto(`${EnvironmentUtil.getMerchantPortalUrl()}/dashboard-merchant-portal-gui/dashboard`);
// Wait for the page to load completely
await page.waitForLoadState('load');
try {
await loginPage.navigate();
await loginPage.login(merchantUser.username, merchantUser.password);

// Record a performance mark
await page.evaluate(() => window.performance.mark('page-visit'));
return browserContext;
} finally {
await page.close();
}
}

// Retrieve performance marks
const marks = await page.evaluate(() =>
JSON.parse(JSON.stringify(window.performance.getEntriesByType('mark')))
);
async function openDashboardPage(browserContext) {
const page = await browserContext.newPage({ headless: false });
const dashboardPage = new DashboardPage(page);

if (marks.length > 0) {
const totalActionTime = marks[0].startTime; // Use startTime of the first mark
metrics[testConfiguration.metrics[0]].add(totalActionTime);
}
} finally {
await page.close(); // Close the page after use
}
});
try {
await dashboardPage.navigate();
await dashboardPage.verifyHeader();

await context.close(); // Close context after the test
return await dashboardPage.getDurationTime();
} finally {
await page.close();
}
}
2 changes: 1 addition & 1 deletion src/tests/product-search/S2_product_search_1.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { group } from 'k6';
import OptionsUtil from '../../utils/options.util';
import { createMetrics } from '../../utils/metric.util';
import { ProductFixture } from '../../fixtures/product.fixture';
import CatalogPage from '../../pages/catalog.page';
import { check } from 'k6';
import EnvironmentUtil from '../../utils/environment.util';
import CatalogPage from '../../pages/yves/catalog.page';

const testConfiguration = {
...EnvironmentUtil.getDefaultTestConfiguration(),
Expand Down
Loading

0 comments on commit a5f473b

Please sign in to comment.