From 4717294d2e32d3efe29ccd4a9ec1f2549c5387d7 Mon Sep 17 00:00:00 2001 From: Vincent T Date: Wed, 24 Jul 2024 11:42:14 -0400 Subject: [PATCH] frontend: Add multi env testing for playwright Signed-off-by: Vincent T --- app/electron/main.ts | 9 +- app/package-lock.json | 74 +++++++++++ app/package.json | 1 + e2e-tests/package-lock.json | 205 +++++++++++++++++++++++++---- e2e-tests/package.json | 11 +- e2e-tests/playwright.config.ts | 7 +- e2e-tests/tests/headlampPage.ts | 26 ++++ e2e-tests/tests/namespaces.spec.ts | 58 ++++++-- e2e-tests/tests/namespacesPage.ts | 43 ++++-- e2e-tests/tsconfig.json | 5 + package-lock.json | 6 + 11 files changed, 393 insertions(+), 52 deletions(-) create mode 100644 e2e-tests/tsconfig.json create mode 100644 package-lock.json diff --git a/app/electron/main.ts b/app/electron/main.ts index bab5cd2024..9ab064e54e 100644 --- a/app/electron/main.ts +++ b/app/electron/main.ts @@ -29,12 +29,19 @@ import windowSize from './windowSize'; dotenv.config({ path: path.join(process.resourcesPath, '.env') }); +let electronStartURL = process.env.ELECTRON_START_URL; + const pathInfoDebug = false; let pathInfo; const isDev = process.env.ELECTRON_DEV || false; let frontendPath = ''; +const isClusterDev = process.env.CLUSTER_DEV || false; +if (isClusterDev) { + electronStartURL = process.env.HEADLAMP_TEST_URL; +} + if (isDev) { frontendPath = path.resolve('..', 'frontend', 'build', 'index.html'); } else { @@ -43,7 +50,7 @@ if (isDev) { const backendToken = randomBytes(32).toString('hex'); const startUrl = ( - process.env.ELECTRON_START_URL || + electronStartURL || url.format({ pathname: frontendPath, protocol: 'file:', diff --git a/app/package-lock.json b/app/package-lock.json index 5d5315a387..ab55ee924b 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -26,6 +26,7 @@ "@babel/preset-typescript": "^7.24.7", "@electron/notarize": "^2.3.2", "@headlamp-k8s/eslint-config": "^0.6.0", + "@playwright/test": "^1.45.3", "electron": "^31.2.0", "electron-builder": "^24.13.3", "i18next-parser": "^9.0.0", @@ -3948,6 +3949,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -11428,6 +11445,38 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -16397,6 +16446,15 @@ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "optional": true }, + "@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "dev": true, + "requires": { + "playwright": "1.45.3" + } + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -22059,6 +22117,22 @@ "find-up": "^4.0.0" } }, + "playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.45.3" + } + }, + "playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "dev": true + }, "plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", diff --git a/app/package.json b/app/package.json index 68d46666cc..40903325fd 100644 --- a/app/package.json +++ b/app/package.json @@ -155,6 +155,7 @@ "@babel/preset-env": "^7.24.8", "@babel/preset-typescript": "^7.24.7", "@electron/notarize": "^2.3.2", + "@playwright/test": "^1.45.3", "electron": "^31.2.0", "electron-builder": "^24.13.3", "i18next-parser": "^9.0.0", diff --git a/e2e-tests/package-lock.json b/e2e-tests/package-lock.json index 4df70c9b70..5d47528aaa 100644 --- a/e2e-tests/package-lock.json +++ b/e2e-tests/package-lock.json @@ -12,23 +12,25 @@ "yaml": "^2.3.4" }, "devDependencies": { - "@playwright/test": "^1.42.1", - "@types/node": "^20.12.2" + "@playwright/test": "^1.45.3", + "@types/node": "^20.12.2", + "cross-env": "^7.0.3" } }, "node_modules/@playwright/test": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz", + "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright": "1.42.1" + "playwright": "1.47.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@types/node": { @@ -40,12 +42,47 @@ "undici-types": "~5.26.4" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "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" @@ -54,34 +91,76 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz", + "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.42.1" + "playwright-core": "1.47.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz", + "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/undici-types": { @@ -90,6 +169,22 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", @@ -101,12 +196,12 @@ }, "dependencies": { "@playwright/test": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.1.tgz", + "integrity": "sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==", "dev": true, "requires": { - "playwright": "1.42.1" + "playwright": "1.47.1" } }, "@types/node": { @@ -118,6 +213,26 @@ "undici-types": "~5.26.4" } }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -125,20 +240,47 @@ "dev": true, "optional": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.1.tgz", + "integrity": "sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.42.1" + "playwright-core": "1.47.1" } }, "playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.47.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.1.tgz", + "integrity": "sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, "undici-types": { @@ -147,6 +289,15 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "yaml": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", diff --git a/e2e-tests/package.json b/e2e-tests/package.json index a5dd2736e3..806895120c 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -3,13 +3,18 @@ "version": "1.0.0", "description": "", "main": "index.js", - "scripts": {}, + "scripts": { + "playwright-test-web": "cross-env PLAYWRIGHT_TEST_MODE=web npx playwright test --ui", + "playwright-test-electron": "cross-env PLAYWRIGHT_TEST_MODE=electron npx playwright test --workers=1", + "playwright-test-incluster": "cross-env PLAYWRIGHT_TEST_MODE=incluster npx playwright test" + }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { - "@playwright/test": "^1.42.1", - "@types/node": "^20.12.2" + "@playwright/test": "^1.45.3", + "@types/node": "^20.12.2", + "cross-env": "^7.0.3" }, "dependencies": { "yaml": "^2.3.4" diff --git a/e2e-tests/playwright.config.ts b/e2e-tests/playwright.config.ts index f94413769d..8188b5df7c 100644 --- a/e2e-tests/playwright.config.ts +++ b/e2e-tests/playwright.config.ts @@ -13,16 +13,16 @@ import { devices } from '@playwright/test'; const config: PlaywrightTestConfig = { testDir: './tests', /* Maximum time one test can run for. */ - timeout: 30 * 1000, + timeout: 60 * 1000, expect: { /** * Maximum time expect() should wait for the condition to be met. * For example in `await expect(locator).toHaveText();` */ - timeout: 5000, + timeout: 10000, }, /* Run tests in files in parallel */ - fullyParallel: true, + fullyParallel: false, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ @@ -37,7 +37,6 @@ const config: PlaywrightTestConfig = { actionTimeout: 0, /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: process.env.HEADLAMP_TEST_URL || 'http://localhost:3000', - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', }, diff --git a/e2e-tests/tests/headlampPage.ts b/e2e-tests/tests/headlampPage.ts index 51ae4a2762..03f5507a2f 100644 --- a/e2e-tests/tests/headlampPage.ts +++ b/e2e-tests/tests/headlampPage.ts @@ -57,6 +57,32 @@ export class HeadlampPage { expect(await pageContent).toContain(text); } + // note: must have minikube started before running these + async startFromMainPage() { + // IF STARTING IN APP MODE IT GOES TO MAIN PAGE NOT OVERVIEW + + // to do: working in cluster mode + + await this.page.waitForLoadState('load'); + + if (process.env.PLAYWRIGHT_TEST_MODE === 'web') { + await this.page.goto('/'); + } + + console.log('MAIN PAGE'); + + await this.page.waitForTimeout(5000); + const currentURL = this.page.url(); + + if (!currentURL.includes('c/minikube')) { + console.log('MORE THAN ONE CLUSTER'); + + await this.page.waitForSelector('a:has-text("minikube")'); + await this.page.getByRole('link', { name: 'minikube' }).click(); + await this.page.waitForLoadState('load'); + } + } + async navigateTopage(page: string, title: RegExp) { await this.page.goto(page); await this.page.waitForLoadState('load'); diff --git a/e2e-tests/tests/namespaces.spec.ts b/e2e-tests/tests/namespaces.spec.ts index 4c0dc313cd..4d8f03df18 100644 --- a/e2e-tests/tests/namespaces.spec.ts +++ b/e2e-tests/tests/namespaces.spec.ts @@ -1,18 +1,60 @@ import { test } from '@playwright/test'; +import path from 'path'; +import { _electron, Page } from 'playwright'; import { HeadlampPage } from './headlampPage'; import { NamespacesPage } from './namespacesPage'; -test('create a namespace with the minimal editor then delete it', async ({ page }) => { +const electronExecutable = process.platform === 'win32' ? 'electron.cmd' : 'electron'; +const electronPath = path.resolve(__dirname, `../../app/node_modules/.bin/${electronExecutable}`); + +const electron = _electron; +const appPath = path.resolve(__dirname, '../../app'); +let electronApp; +let electronPage: Page; + +if (process.env.PLAYWRIGHT_TEST_MODE === 'app') { + console.log('app path'); + console.log(appPath); + + console.log('other path'); + console.log(electronPath); + + test.beforeAll(async () => { + electronApp = await electron.launch({ + cwd: appPath, + executablePath: electronPath, + args: ['.'], + env: { + ...process.env, + NODE_ENV: 'development', + ELECTRON_DEV: 'true', + }, + }); + + electronPage = await electronApp.firstWindow(); + }); + + test.beforeEach(async ({ page }) => { + if (process.env.PLAYWRIGHT_TEST_MODE === 'app') { + page.close(); + } + }); +} + +test('create a namespace with the minimal editor then delete it', async ({ page: browserPage }) => { + const page = process.env.PLAYWRIGHT_TEST_MODE === 'app' ? electronPage : browserPage; const name = 'testing-e2e'; const headlampPage = new HeadlampPage(page); - await headlampPage.authenticate(); - - // If there's no namespaces permission, then we return - const content = await page.content(); - if (!content.includes('Namespaces') || !content.includes('href="/c/main/namespaces')) { - return; + // If we are running in cluster, we need to authenticate + if (process.env.PLAYWRIGHT_TEST_MODE === 'incluster') { + await headlampPage.authenticate(); + // If there's no namespaces permission, then we return + const content = await page.content(); + if (!content.includes('Namespaces') || !content.includes('href="/c/main/namespaces')) { + return; + } } - + await headlampPage.startFromMainPage(); const namespacesPage = new NamespacesPage(page); await namespacesPage.navigateToNamespaces(); await namespacesPage.createNamespace(name); diff --git a/e2e-tests/tests/namespacesPage.ts b/e2e-tests/tests/namespacesPage.ts index 16b5309a63..1b829e03cb 100644 --- a/e2e-tests/tests/namespacesPage.ts +++ b/e2e-tests/tests/namespacesPage.ts @@ -4,8 +4,9 @@ export class NamespacesPage { constructor(private page: Page) {} async navigateToNamespaces() { - await this.page.click('span:has-text("Cluster")'); await this.page.waitForLoadState('load'); + await this.page.waitForSelector('span:has-text("Cluster")'); + await this.page.getByText('Cluster', { exact: true }).click(); await this.page.waitForSelector('span:has-text("Namespaces")'); await this.page.click('span:has-text("Namespaces")'); await this.page.waitForLoadState('load'); @@ -28,35 +29,59 @@ export class NamespacesPage { // This makes it a bit more resilient to flakiness. const pageContent = await this.page.content(); if (pageContent.includes(name)) { - return; + throw new Error(`Test failed: Namespace "${name}" already exists.`); } - await expect(page.getByRole('button', { name: 'Create' })).toBeVisible(); - await page.getByRole('button', { name: 'Create' }).click(); + await page.getByText('Create', { exact: true }).click(); await page.waitForLoadState('load'); - await expect(page.getByText('Use minimal editor')).toBeVisible(); - await page.getByText('Use minimal editor').click(); + // this is a workaround for the checked input not having any unique identifier + const checkedSpan = await page.$('span.Mui-checked'); + + if (!checkedSpan) { + await expect(page.getByText('Use minimal editor')).toBeVisible(); + + await page.getByText('Use minimal editor').click(); + } await page.waitForLoadState('load'); + + await page.waitForSelector('textarea[aria-label="yaml Code"]', { state: 'visible' }); + + await expect(page.getByRole('textbox', { name: 'yaml Code' })).toBeVisible(); await page.fill('textarea[aria-label="yaml Code"]', yaml); await expect(page.getByRole('button', { name: 'Apply' })).toBeVisible(); await page.getByRole('button', { name: 'Apply' }).click(); - await page.waitForSelector(`text=Applied ${name}`); + await page.waitForSelector(`a:has-text("${name}")`); + await expect(page.locator(`a:has-text("${name}")`)).toBeVisible(); } async deleteNamespace(name) { const page = this.page; await page.click('span:has-text("Namespaces")'); await page.waitForLoadState('load'); + await page.waitForSelector(`text=${name}`); await page.click(`a:has-text("${name}")`); - await page.click('button[title="Delete"]'); + + await page.waitForSelector('button[aria-label="Delete"]'); + await page.click('button[aria-label="Delete"]'); + + await page.waitForLoadState('load'); + await page.waitForSelector('text=Are you sure you want to delete this item?'); + await page.waitForSelector('button:has-text("Yes")'); + + await page.waitForLoadState('load'); + await page.click('button:has-text("Yes")'); - await page.waitForSelector(`text=Deleted item ${name}`); + + await page.waitForSelector('h1:has-text("Namespaces")'); + await page.waitForSelector('td:has-text("Terminating")'); + + await expect(page.locator(`a:has-text("${name}")`)).toBeHidden(); } } diff --git a/e2e-tests/tsconfig.json b/e2e-tests/tsconfig.json new file mode 100644 index 0000000000..2f98042715 --- /dev/null +++ b/e2e-tests/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000..3307b745ac --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "headlamp", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}