diff --git a/.gitignore b/.gitignore index bc4262f..10f17c7 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,10 @@ yarn-error.log ############################ coverage +test-results/ +playwright-report/ +blob-report/ +playwright/.cache/ ############################ # Strapi diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4aec7dc --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-playwright.playwright" + ] +} \ No newline at end of file diff --git a/cms/.env.example b/cms/.env.example new file mode 100644 index 0000000..358840a --- /dev/null +++ b/cms/.env.example @@ -0,0 +1,29 @@ + +# Server +HOST=0.0.0.0 +PORT=1337 +NODE_ENV=develop + +# Secrets +APP_KEYS=tobemodified +API_TOKEN_SALT=tobemodified +ADMIN_JWT_SECRET=tobemodified +TRANSFER_TOKEN_SALT=tobemodified + +# Database +DATABASE_CLIENT=postgres +DATABASE_HOST=127.0.0.1 +DATABASE_PORT=5432 +DATABASE_NAME=strapi +DATABASE_USERNAME=tobemodified +DATABASE_PASSWORD=tobemodified +DATABASE_SSL=false +DATABASE_FILENAME= +JWT_SECRET=tobemodified + +# frontend +NEXT_PUBLIC_BACKEND_URL=http://localhost:1337 + +# Test data +DEV_ADMIN_EMAIL=tobemodified +DEV_ADMIN_PASSWORD=tobemodified \ No newline at end of file diff --git a/cms/package-lock.json b/cms/package-lock.json index 62db136..b82ec8c 100644 --- a/cms/package-lock.json +++ b/cms/package-lock.json @@ -23,6 +23,7 @@ "styled-components": "^6.0.0" }, "devDependencies": { + "@playwright/test": "^1.49.1", "@types/fs-extra": "^11.0.4", "@types/node": "^20", "@types/react": "^18", @@ -1936,6 +1937,22 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@playwright/test": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.15.tgz", @@ -15644,6 +15661,53 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/plop": { "version": "2.7.6", "resolved": "https://registry.npmjs.org/plop/-/plop-2.7.6.tgz", diff --git a/cms/package.json b/cms/package.json index a2d55b2..23312d2 100644 --- a/cms/package.json +++ b/cms/package.json @@ -32,6 +32,7 @@ "styled-components": "^6.0.0" }, "devDependencies": { + "@playwright/test": "^1.49.1", "@types/fs-extra": "^11.0.4", "@types/node": "^20", "@types/react": "^18", @@ -45,4 +46,4 @@ "strapi": { "uuid": "54090675-7fba-4ef1-9ec3-07c04f971be5" } -} \ No newline at end of file +} diff --git a/cms/playwright.config.ts b/cms/playwright.config.ts new file mode 100644 index 0000000..e462adc --- /dev/null +++ b/cms/playwright.config.ts @@ -0,0 +1,79 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +import dotenv from 'dotenv'; +import path from 'path'; +dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests/playwright', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ ['html', { open: 'never' }] ], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:1337', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/cms/tests/playwright/strapi-admin-ui-tests.spec.ts b/cms/tests/playwright/strapi-admin-ui-tests.spec.ts new file mode 100644 index 0000000..83d8f2a --- /dev/null +++ b/cms/tests/playwright/strapi-admin-ui-tests.spec.ts @@ -0,0 +1,64 @@ +import { test, expect } from '@playwright/test'; + +export const TEST_USERS = { + adminUser: { + email: process.env.DEV_ADMIN_EMAIL || ``, + password: process.env.DEV_ADMIN_PASSWORD || `` + } +}; + +test.beforeEach(async ({ page }) => { + await page.goto('/'); +}); + +test('unauthorized redirect from localhost:1337 to /admin/auth/login', async ({ page }) => { + await expect(page).toHaveURL('http://localhost:1337/admin/auth/login'); +}); + +test.skip('register admin-user to strapi-admin', async ({ page }) => { + checkTestUserCredentialsExist(); + + await test.step('Check unauthorized redirect to /auth/register-admin', async () => { + await expect(page).toHaveURL('http://localhost:1337/admin/auth/register-admin'); + }); + + await test.step('Fill registration-form fields', async () => { + await page.fill('input[name="firstname"]', 'Adminiy'); + await page.fill('input[name="email"]', TEST_USERS.adminUser.email); + await page.fill('input[name="password"]', TEST_USERS.adminUser.password); + await page.fill('input[name="confirmPassword"]', TEST_USERS.adminUser.password); + await page.click('button[type="submit"]'); + }); + + await test.step('Ensure there are no validation errors displayed under the input fields', async () => { + await expect(page.locator('[data-strapi-field-error="true"]')).toHaveCount(0); + }); + + await test.step('Verify redirection to the authorized admin dashboard with the welcome message', async () => { + await expect(page.locator('//h1')).toHaveText('Welcome 👋'); + }); +}); + +test('login to strapi-admin', async ({ page }) => { + checkTestUserCredentialsExist(); + + await test.step('Fill in the email and password fields & press Login button', async () => { + await page.fill('input[name="email"]', TEST_USERS.adminUser.email); + await page.fill('input[name="password"]', TEST_USERS.adminUser.password); + await page.click('button[type="submit"]'); + }); + + await test.step('Ensure there are no validation errors displayed under the input fields', async () => { + await expect(page.locator('[data-strapi-field-error="true"]')).toHaveCount(0); + }); + + await test.step('Verify redirection to the authorized admin dashboard with the welcome message', async () => { + await expect(page.locator('//h1')).toHaveText('Welcome 👋'); + }); +}); + +async function checkTestUserCredentialsExist() { + if (!TEST_USERS.adminUser.email || !TEST_USERS.adminUser.password) { + throw new Error('Missing test user credentials in .env file'); + } +} \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index 7b8da95..75f36c5 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -40,3 +40,7 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 95c63b0..ebdd9cc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@playwright/test": "^1.49.1", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -315,9 +316,8 @@ "version": "1.49.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { "playwright": "1.49.1" }, @@ -1982,13 +1982,13 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -3162,9 +3162,8 @@ "version": "1.49.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "dependencies": { "playwright-core": "1.49.1" }, @@ -3182,9 +3181,8 @@ "version": "1.49.1", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, - "peer": true, "bin": { "playwright-core": "cli.js" }, diff --git a/frontend/package.json b/frontend/package.json index d3eb8c8..3bf28b3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,6 +23,7 @@ "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.1.3", - "typescript": "^5" + "typescript": "^5", + "@playwright/test": "^1.49.1" } } diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 0000000..3330bd6 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,79 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests/playwright', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ ['html', { open: 'never' }] ], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/frontend/tests/playwright/baseTest.ts b/frontend/tests/playwright/baseTest.ts new file mode 100644 index 0000000..0421550 --- /dev/null +++ b/frontend/tests/playwright/baseTest.ts @@ -0,0 +1,58 @@ +import { test as base, expect, Page } from '@playwright/test'; + +export const PAGE_BASE_DATA = { + pageTitleText: 'Дом молитвы', + pageMetaDescriptionText: 'Церковь Евангельских христиан-баптистов', +}; + +type CustomFixtures = { + page: Page; + consoleErrors: string[]; +}; + +export const test = base.extend({ + consoleErrors: async ({}, use) => { + const errors: string[] = []; + await use(errors); + }, + + page: async ({ page, consoleErrors }, use) => { + // Catch errors in console + page.on('console', msg => { + if (msg.type() === 'error') { + consoleErrors.push(msg.text()); + } + }); + + // Catch unhandled exceptions on page + page.on('pageerror', error => { + consoleErrors.push(error.message); + }); + + await use(page); + } +}); + +// Existing console errors check +export const checkConsoleErrors = () => { + test('check console errors', async ({ consoleErrors, page }) => { + await page.waitForLoadState('networkidle'); + expect(consoleErrors, 'Found errors on the page').toHaveLength(0); + }); +}; + +// Check base elements on pages +export const checkBaseElements = () => { + test('check base elements', async ({ page }) => { + // Wait for the page to load completely + await page.waitForLoadState('networkidle'); + + // Check the page title + const pageTitle = await page.title(); + expect(pageTitle).toBe(PAGE_BASE_DATA.pageTitleText); + + // Check the meta description + const metaDescription = await page.getAttribute('meta[name="description"]', 'content'); + expect(metaDescription).toBe(PAGE_BASE_DATA.pageMetaDescriptionText); + }); +}; \ No newline at end of file diff --git a/frontend/tests/playwright/landing-page-test.spec.ts b/frontend/tests/playwright/landing-page-test.spec.ts new file mode 100644 index 0000000..ab48255 --- /dev/null +++ b/frontend/tests/playwright/landing-page-test.spec.ts @@ -0,0 +1,103 @@ +import { test, checkConsoleErrors, checkBaseElements } from './baseTest'; +import { expect } from '@playwright/test'; + +export const PAGE_DATA = { + // TODO: use texts from constants.ts class when the page will be ready + headingWelcomeText: 'Добро пожаловать в Дом молитвы', + divDomMolitvyText: 'Дом молитвы - протестантская церковь, часть братства евангельских христиан баптистов', + buttonReadMoreText: 'Узнать больше', + headingAboutUsText: 'О нас', + headingHowToBecomeChristianText: 'Как стать христианином?', + headingOurServicesText: 'Наши служения' +}; + +let response: any; + +test.beforeEach(async ({ page }) => { + + const browserContext = page.context(); + browserContext.addCookies([ + { + name: 'debugCookie', + value: 'true', + domain: 'localhost', + path: '/', + expires: -1, + httpOnly: true, + secure: false, + sameSite: 'Lax', + }, + ]); + + response = await page.goto('/'); +}); + +checkConsoleErrors(); + +checkBaseElements(); + +test('check headers on landing page', async ({ page }) => { + test.slow(); + + if (response) { + const headers = response.headers(); + + expect.soft(response.status()).toBe(200); + expect.soft(headers['etag']).toBeInstanceOf(String); + expect.soft(headers['x-nextjs-stale-time']).toBeInstanceOf(String); + expect.soft(headers['x-nextjs-prerender']).toBe('1'); + expect.soft(headers['x-powered-by']).toBe('Next.js'); + expect.soft(headers['cache-control']).toBe('s-maxage=31536000'); + expect.soft(headers['transfer-encoding']).toBe('chunked'); + } else { + throw new Error('No HTTP response received'); + } +}); + +test('check elements on test landing page', async ({ page }) => { + await test.step('check heading: header', async () => { + await expect(page.locator('xpath=//header[text()="header"]')).toBeVisible(); + }); + + await test.step('check div: test', async () => { + await expect(page.locator('xpath=//div[text()="test"]')).toBeVisible(); + }); + + await test.step('check footer: footer', async () => { + await expect(page.locator('xpath=//footer[text()="footer"]')).toBeVisible(); + }); +}); + +test.skip('check elements on landing page', { + tag: '@draft' +}, async ({ page }) => { + // TODO: change locators to getByRole or XPath when the page will be ready & remove '.skip' + + await test.step('check heading: Welcome', async () => { + await expect(page.getByText(PAGE_DATA.headingWelcomeText)).toBeVisible(); + }); + + await test.step('check div: Dom Molitvy', async () => { + await expect(page.getByText(PAGE_DATA.divDomMolitvyText)).toBeVisible(); + }); + + await test.step('check button: Read More', async () => { + await expect(page.getByText(PAGE_DATA.buttonReadMoreText)).toBeVisible(); + }); + + await test.step('check heading: About Us', async () => { + await expect(page.getByText(PAGE_DATA.headingAboutUsText)).toBeVisible(); + }); + + await test.step('check heading: How to become christian', async () => { + await expect(page.getByText(PAGE_DATA.headingHowToBecomeChristianText)).toBeVisible(); + }); + + await test.step('check heading: Our services', async () => { + await expect(page.getByText(PAGE_DATA.headingOurServicesText)).toBeVisible(); + }); +}); + +test('should match snapshot', async ({ page }) => { + await expect(await page.screenshot()).toMatchSnapshot('landing-page-snapshot.png'); +}); \ No newline at end of file diff --git a/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-darwin.png b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-darwin.png new file mode 100644 index 0000000..01a990f Binary files /dev/null and b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-darwin.png differ diff --git a/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-win32.png b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-win32.png new file mode 100644 index 0000000..02715d8 Binary files /dev/null and b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-chromium-win32.png differ diff --git a/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-firefox-darwin.png b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-firefox-darwin.png new file mode 100644 index 0000000..7434393 Binary files /dev/null and b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-firefox-darwin.png differ diff --git a/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-webkit-darwin.png b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-webkit-darwin.png new file mode 100644 index 0000000..6e7903b Binary files /dev/null and b/frontend/tests/playwright/landing-page-test.spec.ts-snapshots/landing-page-snapshot-webkit-darwin.png differ diff --git a/frontend/tests/playwright/stub-page-test.spec.ts b/frontend/tests/playwright/stub-page-test.spec.ts new file mode 100644 index 0000000..6624390 --- /dev/null +++ b/frontend/tests/playwright/stub-page-test.spec.ts @@ -0,0 +1,22 @@ +import { test, checkConsoleErrors, checkBaseElements } from './baseTest'; +import { expect } from '@playwright/test'; + +export const PAGE_DATA = { + headerText: 'Скоро здесь будет сайт' +}; + +test.beforeEach(async ({ page }) => { + await page.goto('/'); +}); + +checkConsoleErrors(); + +checkBaseElements(); + +test('has title', async ({ page }) => { + await expect(page.getByRole('heading', {name: PAGE_DATA.headerText})).toBeVisible(); +}); + +test('should match snapshot', async ({ page }) => { + await expect(await page.screenshot()).toMatchSnapshot('stub-page-snapshot.png'); +}); \ No newline at end of file diff --git a/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-darwin.png b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-darwin.png new file mode 100644 index 0000000..56f2bdc Binary files /dev/null and b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-darwin.png differ diff --git a/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-win32.png b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-win32.png new file mode 100644 index 0000000..4153999 Binary files /dev/null and b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-chromium-win32.png differ diff --git a/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-firefox-darwin.png b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-firefox-darwin.png new file mode 100644 index 0000000..8d77ba7 Binary files /dev/null and b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-firefox-darwin.png differ diff --git a/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-webkit-darwin.png b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-webkit-darwin.png new file mode 100644 index 0000000..b57839e Binary files /dev/null and b/frontend/tests/playwright/stub-page-test.spec.ts-snapshots/stub-page-snapshot-webkit-darwin.png differ