diff --git a/package-lock.json b/package-lock.json
index 2cb72b3c..ed1a3a52 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9620,6 +9620,23 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
+ "node_modules/@playwright/test": {
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
+ "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "playwright": "1.59.1"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@pnpm/config.env-replace": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
@@ -24504,12 +24521,13 @@
}
},
"node_modules/playwright": {
- "version": "1.56.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz",
- "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==",
- "dev": true,
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
+ "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
+ "devOptional": true,
+ "license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.56.1"
+ "playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
@@ -24522,10 +24540,11 @@
}
},
"node_modules/playwright-core": {
- "version": "1.56.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz",
- "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==",
- "dev": true,
+ "version": "1.59.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
+ "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
+ "devOptional": true,
+ "license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
@@ -29505,6 +29524,7 @@
"web-haptics": "^0.0.6"
},
"devDependencies": {
+ "@playwright/test": "^1.59.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
diff --git a/src/webapp/.gitignore b/src/webapp/.gitignore
new file mode 100644
index 00000000..335bd46d
--- /dev/null
+++ b/src/webapp/.gitignore
@@ -0,0 +1,8 @@
+
+# Playwright
+node_modules/
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
+/playwright/.auth/
diff --git a/src/webapp/e2e/auth-redirect.spec.ts b/src/webapp/e2e/auth-redirect.spec.ts
new file mode 100644
index 00000000..56576c35
--- /dev/null
+++ b/src/webapp/e2e/auth-redirect.spec.ts
@@ -0,0 +1,26 @@
+import { test, expect } from '@playwright/test';
+
+test('unauthenticated /settings visit redirects to /login', async ({
+ page,
+}) => {
+ const response = await page.goto('/settings');
+ console.log('status:', response?.status(), 'final url:', page.url());
+
+ await expect(page).toHaveURL('/login');
+});
+
+test('unauthenticated /notifications visit redirects to /login', async ({
+ page,
+}) => {
+ const response = await page.goto('/notifications');
+ console.log('status:', response?.status(), 'final url:', page.url());
+
+ await expect(page).toHaveURL('/login');
+});
+
+test('unauthenticated /home visit redirects to /login', async ({ page }) => {
+ const response = await page.goto('/home');
+ console.log('status:', response?.status(), 'final url:', page.url());
+
+ await expect(page).toHaveURL('/login');
+});
diff --git a/src/webapp/e2e/example.spec.ts b/src/webapp/e2e/example.spec.ts
new file mode 100644
index 00000000..f71a6759
--- /dev/null
+++ b/src/webapp/e2e/example.spec.ts
@@ -0,0 +1,20 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Expect a title "to contain" a substring.
+ await expect(page).toHaveTitle(/Playwright/);
+});
+
+test('get started link', async ({ page }) => {
+ await page.goto('https://playwright.dev/');
+
+ // Click the get started link.
+ await page.getByRole('link', { name: 'Get started' }).click();
+
+ // Expects page to have a heading with the name of Installation.
+ await expect(
+ page.getByRole('heading', { name: 'Installation' }),
+ ).toBeVisible();
+});
diff --git a/src/webapp/next-env.d.ts b/src/webapp/next-env.d.ts
index 9edff1c7..c4b7818f 100644
--- a/src/webapp/next-env.d.ts
+++ b/src/webapp/next-env.d.ts
@@ -1,6 +1,6 @@
///
///
-import "./.next/types/routes.d.ts";
+import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/src/webapp/package.json b/src/webapp/package.json
index c0a2a750..63369623 100644
--- a/src/webapp/package.json
+++ b/src/webapp/package.json
@@ -55,6 +55,7 @@
"web-haptics": "^0.0.6"
},
"devDependencies": {
+ "@playwright/test": "^1.59.1",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
diff --git a/src/webapp/playwright.config.ts b/src/webapp/playwright.config.ts
new file mode 100644
index 00000000..0275b42d
--- /dev/null
+++ b/src/webapp/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: './e2e',
+ /* 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',
+ /* 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: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:4000/',
+
+ /* 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://localhost:3000',
+ // reuseExistingServer: !process.env.CI,
+ // },
+});