E2e tests frontend (#3473)

* add playwright

* add e2e test

* add test

* add e2e test

* add e2e tests

* fixed config
This commit is contained in:
Andrey Babushkin 2025-06-03 14:32:28 +02:00 committed by GitHub
parent c35ace3544
commit 373238429e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 61 additions and 166 deletions

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ node_modules
.idea
*.mob*
install-state.gz
frontend/tests/playwright/auth-state.json

View file

@ -132,7 +132,7 @@
"cssnano": "^7.0.7",
"cypress": "13.17.0",
"cypress-image-snapshot": "^4.0.1",
"dotenv": "^6.2.0",
"dotenv": "^16.5.0",
"esbuild-loader": "^4.3.0",
"eslint": "^9.21.0",
"eslint-import-resolver-typescript": "^3.8.3",

View file

@ -5,6 +5,7 @@ import { defineConfig, devices } from '@playwright/test';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/playwright',
fullyParallel: true,

View file

@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}

View file

@ -1,70 +0,0 @@
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:3333",
"localStorage": [
{
"name": "__$session-timezone$_local__",
"value": "true"
},
{
"name": "i18nextLng",
"value": "en"
},
{
"name": "theme",
"value": "light"
},
{
"name": "__$user-gettingStarted$__",
"value": "{\"steps\":[{\"title\":\"🛠️ Install OpenReplay\",\"status\":\"completed\"},{\"title\":\"🕵️ Identify Users\",\"status\":\"completed\"},{\"title\":\"🧑‍💻 Invite Team Members\",\"status\":\"completed\"},{\"title\":\"🔌 Integrations\",\"status\":\"completed\"}],\"status\":\"completed\"}"
},
{
"name": "__$global-destinationPath$__",
"value": "/"
},
{
"name": "___$or_spotToken$___",
"value": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ4ODc2MzkyLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODg3NTc5MiwiYXVkIjoic3BvdDpPcGVuUmVwbGF5In0.5Xuboo2h30P6mTYHEtzoaJTBZvZGwEMs8ywookDsY0Xp0Ah9m9K-s3WF2x-M_7LCfDOp7nBFa8j9AyKz09V0oA"
},
{
"name": "__$session-timezone$__",
"value": "{\"label\":\"UTC +02:00\",\"value\":\"UTC+02\"}"
},
{
"name": "__$session-mouseTrail$__",
"value": "true"
},
{
"name": "__openreplay_health_status",
"value": "1748875801944"
},
{
"name": "__$user-siteId$__",
"value": "109"
},
{
"name": "__or__langBannerClosed",
"value": "0"
},
{
"name": "AuthStore",
"value": "{\"authDetails\":\"{\\\"tenants\\\":true,\\\"sso\\\":null,\\\"ssoProvider\\\":null,\\\"enforceSSO\\\":null,\\\"edition\\\":\\\"foss\\\"}\",\"__mps__\":{\"expireInTimestamp\":1748879400231}}"
},
{
"name": "UserStore",
"value": "{\"siteId\":null,\"tenants\":[],\"jwt\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ4OTYyMTkyLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODg3NTc5MiwiYXVkIjoiZnJvbnQ6T3BlblJlcGxheSJ9.bfMw80k15BIwHkR_JQsY_DFqDJwERZcpYLOBRbcPcm2OT_WPozDal6HS8rs5YeyW0m98HRJa1ShGoMiyQhxMJA\",\"spotJwt\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ4ODc2MzkyLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODg3NTc5MiwiYXVkIjoic3BvdDpPcGVuUmVwbGF5In0.5Xuboo2h30P6mTYHEtzoaJTBZvZGwEMs8ywookDsY0Xp0Ah9m9K-s3WF2x-M_7LCfDOp7nBFa8j9AyKz09V0oA\",\"scopeState\":2,\"onboarding\":false,\"account\":\"{\\\"id\\\":58,\\\"email\\\":\\\"andrei@openreplay.com\\\",\\\"smtp\\\":false,\\\"expirationDate\\\":-1,\\\"permissions\\\":[],\\\"settings\\\":{\\\"modules\\\":[\\\"usability-tests\\\",\\\"feature-flags\\\"]},\\\"iceServers\\\":[],\\\"hasPassword\\\":true,\\\"apiKey\\\":\\\"48Vph82zUEWHmfPSUbgG\\\",\\\"edition\\\":\\\"foss\\\",\\\"optOut\\\":false,\\\"versionNumber\\\":\\\"1.17.0\\\",\\\"name\\\":\\\"Andrei\\\",\\\"createdAt\\\":1652690354756,\\\"admin\\\":true,\\\"superAdmin\\\":false}\"}"
},
{
"name": "__openreplay_health_response",
"value": "{\"overallHealth\":true,\"healthMap\":{\"databases\":{\"name\":\"Databases\",\"healthOk\":true,\"subservices\":{\"postgres\":{\"health\":true,\"details\":{}}},\"serviceName\":\"databases\"},\"ingestionPipeline\":{\"name\":\"Ingestion Pipeline\",\"healthOk\":true,\"subservices\":{\"redis\":{\"health\":true,\"details\":{}}},\"serviceName\":\"ingestionPipeline\"},\"backendServices\":{\"name\":\"Backend Services\",\"healthOk\":true,\"subservices\":{\"alerts\":{\"health\":true,\"details\":{}},\"assets\":{\"health\":true,\"details\":{}},\"assist\":{\"health\":true,\"details\":{}},\"chalice\":{\"health\":true,\"details\":{}},\"db\":{\"health\":true,\"details\":{}},\"ender\":{\"health\":true,\"details\":{}},\"frontend\":{\"health\":true,\"details\":{}},\"heuristics\":{\"health\":true,\"details\":{}},\"http\":{\"health\":true,\"details\":{}},\"ingress-nginx\":{\"health\":true,\"details\":{}},\"integrations\":{\"health\":true,\"details\":{}},\"sink\":{\"health\":true,\"details\":{}},\"sourcemapreader\":{\"health\":true,\"details\":{}},\"storage\":{\"health\":true,\"details\":{}}},\"serviceName\":\"backendServices\"}},\"details\":{\"numberOfSessionsCaptured\":216638,\"numberOfEventCaptured\":1840149}}"
},
{
"name": "__$session-filter$__",
"value": "{\"name\":\"\",\"events\":[],\"custom\":{},\"rangeValue\":\"LAST_24_HOURS\",\"startDate\":1748790000000,\"endDate\":1748876400000,\"groupByUser\":false,\"sort\":\"startTs\",\"order\":\"desc\",\"strict\":false,\"eventsOrder\":\"then\",\"limit\":10,\"page\":1,\"perPage\":10,\"tab\":\"sessions\",\"filters\":[]}"
}
]
}
]
}

View file

@ -1,5 +1,7 @@
import { authStateFile, testUseAuthState } from './helpers';
import { expect, test as setup } from '@playwright/test';
import * as dotenv from 'dotenv';
dotenv.config();
testUseAuthState();
@ -8,6 +10,9 @@ setup.beforeEach(async ({ page }) => {
});
setup('authenticate', async ({ page }) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('/login');
try {
@ -15,14 +20,9 @@ setup('authenticate', async ({ page }) => {
console.log('Current URL:', url);
if (url.includes('login')) {
console.log('Already on login page, skipping authentication');
await page.locator('[data-test-id="login"]').click();
await page.locator('.ant-input-affix-wrapper').first().click();
await page
.locator('[data-test-id="login"]')
.fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').click();
await page.locator('[data-test-id="password"]').fill('Andrey123!');
console.log('On login page, authenticating...');
await page.locator('[data-test-id="login"]').fill(LOGIN);
await page.locator('[data-test-id="password"]').fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
}
await expect(page.getByRole('heading', { name: 'Sessions' })).toBeVisible();

View file

@ -1,9 +1,13 @@
import { test, expect } from '@playwright/test';
test('Check if dashboards exist', async ({ page }) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('http://localhost:3333/login');
await page.locator('[data-test-id="login"]').fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').fill('Andrey123!');
await page
.locator('[data-test-id="login"]')
.fill(LOGIN);
await page.locator('[data-test-id="password"]').fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
await page.getByText('Dashboards').click();
await page.getByText('Renamed One').click();

View file

@ -1,16 +1,22 @@
import { test, expect } from '@playwright/test';
import { test } from '@playwright/test';
import { testUseAuthState } from './helpers';
testUseAuthState();
test('check session list after change period', async ({ page }) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('http://localhost:3333/login');
await page.locator('[data-test-id="login"]').fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').fill('Andrey123!');
await page.locator('[data-test-id="login"]').fill(LOGIN);
await page
.locator('[data-test-id="password"]')
.fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
await page.getByRole('button', { name: 'Android caret-down' }).click();
await page.getByRole('menuitem', { name: 'OpenReplay Documentation Site' }).click();
await page
.getByRole('menuitem', { name: 'OpenReplay Documentation Site' })
.click();
await page.getByRole('button', { name: 'Past 24 Hours down' }).click();
await page.getByRole('menuitem', { name: 'Past 30 Days' }).click();
await page.locator('#session-item').first().click();
});
});

View file

@ -1,9 +1,13 @@
import { test, expect } from '@playwright/test';
test('Sign in flow', async ({ page }) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('/');
await page.locator('[data-test-id="login"]').fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').fill('Andrey123!');
await page
.locator('[data-test-id="login"]')
.fill(LOGIN);
await page.locator('[data-test-id="password"]').fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
await expect(page.getByRole('heading', { name: 'Sessions' })).toBeVisible();
});

View file

@ -1,9 +1,13 @@
import { test, expect } from '@playwright/test';
test('Spots should display', async ({ page }) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('http://localhost:3333/login');
await page.locator('[data-test-id="login"]').fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').fill('Andrey123!');
await page
.locator('[data-test-id="login"]')
.fill(LOGIN);
await page.locator('[data-test-id="password"]').fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
await page.getByText('Spots').click();
await page.waitForTimeout(1000);

View file

@ -1,12 +1,19 @@
import { test, expect } from '@playwright/test';
test('The freshest session from openreplay website doesnt have white screen', async ({ page }) => {
test('The freshest session from openreplay website doesnt have white screen', async ({
page,
}) => {
const LOGIN = process.env.TEST_FOSS_LOGIN || '';
const PASSWORD = process.env.TEST_FOSS_PASSWORD || '';
await page.goto('http://localhost:3333/login');
await page.locator('[data-test-id="login"]').fill('andrei@openreplay.com');
await page.locator('[data-test-id="password"]').fill('Andrey123!');
await page.locator('[data-test-id="login"]').fill(LOGIN);
await page.locator('[data-test-id="password"]').fill(PASSWORD);
await page.locator('[data-test-id="log-button"]').click();
await page.waitForTimeout(1000);
await page.locator('[data-test-id="session-list-header"]').locator('[data-test-id="widget-select-date-range"]').click();
await page
.locator('[data-test-id="session-list-header"]')
.locator('[data-test-id="widget-select-date-range"]')
.click();
await page.getByText('Past 30 Days').click();
await page.locator('[data-test-id="project-dropdown"]').click();
await page.getByRole('button', { name: 'Android caret-down' }).click();
@ -17,7 +24,7 @@ test('The freshest session from openreplay website doesnt have white screen', as
if (borderBlocks.length >= 2) {
const secondBlock = borderBlocks[1];
const playButton = await secondBlock.$('#play-button');
if (playButton) {
const link = await playButton.$('a');
if (link) {

View file

@ -7158,10 +7158,10 @@ __metadata:
languageName: node
linkType: hard
"dotenv@npm:^6.2.0":
version: 6.2.0
resolution: "dotenv@npm:6.2.0"
checksum: 10c1/a7e8bf58954d8d46dfd5c3accb8643b5b9d2e683da66515cd0abd8ff60735d4d21e4f11debbdbc19fa6243c85fb938eb4b590d5563c53a269ade81125444a292
"dotenv@npm:^16.5.0":
version: 16.5.0
resolution: "dotenv@npm:16.5.0"
checksum: 10c1/95956f0c08c33c134ada7323006fd4db047294689b4a6d7714ebd2cbeb12a4888b2d70a7ac89fdd68c39bceb95f600e74eebb0671c6f31bab87ec9a221f980ba
languageName: node
linkType: hard
@ -12961,7 +12961,7 @@ __metadata:
cssnano: "npm:^7.0.7"
cypress: "npm:13.17.0"
cypress-image-snapshot: "npm:^4.0.1"
dotenv: "npm:^6.2.0"
dotenv: "npm:^16.5.0"
echarts: "npm:^5.6.0"
esbuild-loader: "npm:^4.3.0"
eslint: "npm:^9.21.0"

View file

@ -1,66 +0,0 @@
{
"cookies": [],
"origins": [
{
"origin": "http://localhost:3333",
"localStorage": [
{
"name": "__$session-timezone$_local__",
"value": "true"
},
{
"name": "i18nextLng",
"value": "en"
},
{
"name": "theme",
"value": "light"
},
{
"name": "__$user-gettingStarted$__",
"value": "{\"steps\":[{\"title\":\"🛠️ Install OpenReplay\",\"status\":\"completed\"},{\"title\":\"🕵️ Identify Users\",\"status\":\"completed\"},{\"title\":\"🧑‍💻 Invite Team Members\",\"status\":\"completed\"},{\"title\":\"🔌 Integrations\",\"status\":\"completed\"}],\"status\":\"completed\"}"
},
{
"name": "___$or_spotToken$___",
"value": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ4OTQ1MTQxLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODk0NDU0MSwiYXVkIjoic3BvdDpPcGVuUmVwbGF5In0._QEHvIc8ShH0PsRPtDQAo50Dc-H-Adpu8CZKXQsPF31GSLUl5SS9MV92xntRxfcloigRA1Hz2F817EF5jrgNJg"
},
{
"name": "__$session-timezone$__",
"value": "{\"label\":\"UTC +02:00\",\"value\":\"UTC+02\"}"
},
{
"name": "__$session-mouseTrail$__",
"value": "true"
},
{
"name": "__openreplay_health_status",
"value": "1748944551307"
},
{
"name": "__$user-siteId$__",
"value": "65"
},
{
"name": "__or__langBannerClosed",
"value": "0"
},
{
"name": "AuthStore",
"value": "{\"authDetails\":\"{\\\"tenants\\\":true,\\\"sso\\\":null,\\\"ssoProvider\\\":null,\\\"enforceSSO\\\":null,\\\"edition\\\":\\\"foss\\\"}\",\"__mps__\":{\"expireInTimestamp\":1748948149584}}"
},
{
"name": "UserStore",
"value": "{\"siteId\":null,\"tenants\":[],\"jwt\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ5MDMwOTQxLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODk0NDU0MSwiYXVkIjoiZnJvbnQ6T3BlblJlcGxheSJ9.YHb2kldXFPzP2ecGoyPOo6I7_KH0BqhimOQKa1VtvSe_LTf2AzQNvKAYmsnx6-55lWX_b4wV5g4s4cdsYexOdw\",\"spotJwt\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjU4LCJ0ZW5hbnRJZCI6MSwiZXhwIjoxNzQ4OTQ1MTQxLCJpc3MiOiJPcGVuUmVwbGF5LW9zcyIsImlhdCI6MTc0ODk0NDU0MSwiYXVkIjoic3BvdDpPcGVuUmVwbGF5In0._QEHvIc8ShH0PsRPtDQAo50Dc-H-Adpu8CZKXQsPF31GSLUl5SS9MV92xntRxfcloigRA1Hz2F817EF5jrgNJg\",\"scopeState\":2,\"onboarding\":false,\"account\":\"{\\\"id\\\":58,\\\"email\\\":\\\"andrei@openreplay.com\\\",\\\"smtp\\\":false,\\\"expirationDate\\\":-1,\\\"permissions\\\":[],\\\"settings\\\":{\\\"modules\\\":[\\\"usability-tests\\\",\\\"feature-flags\\\"]},\\\"iceServers\\\":[],\\\"hasPassword\\\":true,\\\"apiKey\\\":\\\"48Vph82zUEWHmfPSUbgG\\\",\\\"edition\\\":\\\"foss\\\",\\\"optOut\\\":false,\\\"versionNumber\\\":\\\"1.17.0\\\",\\\"name\\\":\\\"Andrei\\\",\\\"createdAt\\\":1652690354756,\\\"admin\\\":true,\\\"superAdmin\\\":false}\"}"
},
{
"name": "__openreplay_health_response",
"value": "{\"overallHealth\":true,\"healthMap\":{\"databases\":{\"name\":\"Databases\",\"healthOk\":true,\"subservices\":{\"postgres\":{\"health\":true,\"details\":{}}},\"serviceName\":\"databases\"},\"ingestionPipeline\":{\"name\":\"Ingestion Pipeline\",\"healthOk\":true,\"subservices\":{\"redis\":{\"health\":true,\"details\":{}}},\"serviceName\":\"ingestionPipeline\"},\"backendServices\":{\"name\":\"Backend Services\",\"healthOk\":true,\"subservices\":{\"alerts\":{\"health\":true,\"details\":{}},\"assets\":{\"health\":true,\"details\":{}},\"assist\":{\"health\":true,\"details\":{}},\"chalice\":{\"health\":true,\"details\":{}},\"db\":{\"health\":true,\"details\":{}},\"ender\":{\"health\":true,\"details\":{}},\"frontend\":{\"health\":true,\"details\":{}},\"heuristics\":{\"health\":true,\"details\":{}},\"http\":{\"health\":true,\"details\":{}},\"ingress-nginx\":{\"health\":true,\"details\":{}},\"integrations\":{\"health\":true,\"details\":{}},\"sink\":{\"health\":true,\"details\":{}},\"sourcemapreader\":{\"health\":true,\"details\":{}},\"storage\":{\"health\":true,\"details\":{}}},\"serviceName\":\"backendServices\"}},\"details\":{\"numberOfSessionsCaptured\":216813,\"numberOfEventCaptured\":1841202}}"
},
{
"name": "__$session-filter$__",
"value": "{\"name\":\"\",\"events\":[],\"custom\":{},\"rangeValue\":\"LAST_30_DAYS\",\"startDate\":1746352800000,\"endDate\":1748944800000,\"groupByUser\":false,\"sort\":\"startTs\",\"order\":\"desc\",\"strict\":false,\"eventsOrder\":\"then\",\"limit\":10,\"rangeName\":\"LAST_30_DAYS\",\"page\":1,\"perPage\":10,\"tab\":\"sessions\",\"filters\":[{\"type\":\"location\",\"isEvent\":true,\"value\":[\"\"],\"operator\":\"isAny\",\"source\":\"\",\"sourceOperator\":\"\",\"filters\":[]}]}"
}
]
}
]
}