From 4b36b607e39f0fb45561243b8fe4d3672f61c56f Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Tue, 2 Apr 2024 09:43:34 +0200 Subject: [PATCH 01/15] feat: playwright init --- .github/workflows/playwright.yml | 27 ++++++++++ .gitignore | 4 ++ app/main.dev.js | 10 ++-- package.json | 2 + playwright-report/index.html | 68 +++++++++++++++++++++++ playwright.config.js | 45 ++++++++++++++++ signup.png | Bin 0 -> 42027 bytes test/e2e/.prettierrc | 3 ++ test/e2e/electron.test.js | 90 +++++++++++++++++++++++++++++++ yarn.lock | 48 ++++++++++++++--- 10 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright-report/index.html create mode 100644 playwright.config.js create mode 100644 signup.png create mode 100644 test/e2e/.prettierrc create mode 100644 test/e2e/electron.test.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 000000000..f7f7c1c41 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm install -g yarn && yarn + - name: Install Playwright Browsers + run: yarn playwright install --with-deps + - name: Run Playwright tests + run: yarn playwright test + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 65285681a..4f3e09c7e 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ _book/ web/ \.vscode/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/app/main.dev.js b/app/main.dev.js index b3efbef5d..d0b163669 100755 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -76,11 +76,11 @@ if (process.env.NODE_ENV === 'development') { require('module').globalPaths.push(p); // eslint-disable-line process.env.APPIMAGE = path.join(__dirname, 'release'); autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml'); - Object.defineProperty(app, 'isPackaged', { - get() { - return true; - } - }); + // Object.defineProperty(app, 'isPackaged', { + // get() { + // return true; + // } + // }); } app.on('window-all-closed', () => { diff --git a/package.json b/package.json index 5acf1126a..45c17016b 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,9 @@ "@babel/runtime-corejs2": "7.23.9", "@electron/notarize": "2.2.1", "@jest-runner/electron": "3.0.1", + "@playwright/test": "^1.42.1", "@tidepool/direct-io": "3.0.2", + "@types/node": "^20.11.30", "aws-sdk": "2.1544.0", "babel-core": "7.0.0-bridge.0", "babel-jest": "26.6.3", diff --git a/playwright-report/index.html b/playwright-report/index.html new file mode 100644 index 000000000..b6cd673a1 --- /dev/null +++ b/playwright-report/index.html @@ -0,0 +1,68 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 000000000..375882d15 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,45 @@ +// @ts-check +const { defineConfig, devices } = require('@playwright/test'); + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = defineConfig({ + testDir: './test/e2e', + /* Run tests in files in parallel */ + // globalTimeout: 10000, + timeout: 15000, + expect: { + timeout: 5000 + }, + 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 */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: 1, + /* 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: { + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + projects: [ + { + name: 'Mocked', + use: { + /* Mock the network */ + mode: 'default' + } + } + ] +}); + diff --git a/signup.png b/signup.png new file mode 100644 index 0000000000000000000000000000000000000000..3038b15d37ddb1c7d0d954ea83da8c21b0cbe70f GIT binary patch literal 42027 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 literal 0 HcmV?d00001 diff --git a/test/e2e/.prettierrc b/test/e2e/.prettierrc new file mode 100644 index 000000000..1e50ff1c8 --- /dev/null +++ b/test/e2e/.prettierrc @@ -0,0 +1,3 @@ +{ + "tabWidth": 2 +} \ No newline at end of file diff --git a/test/e2e/electron.test.js b/test/e2e/electron.test.js new file mode 100644 index 000000000..cb40fd667 --- /dev/null +++ b/test/e2e/electron.test.js @@ -0,0 +1,90 @@ +const { test, _electron: electron, expect } = require('@playwright/test') + +// @ts-check +test.describe('Home screen', () => { + /** @type {import('@playwright/test').Page} */ + let window; + /** @type {import('@playwright/test').ElectronApplication} */ + let electronApp; + test.beforeEach(async() => { + electronApp = await electron.launch({ args: ['./app/main.prod.js'] }); + const appPath = await electronApp.evaluate(async ({ app }) => { + // This runs in the main Electron process, parameter here is always + // the result of the require('electron') in the main app script. + return app.getAppPath(); + }); + console.log(appPath); + + // Get the first window that the app opens, wait if necessary. + window = await electronApp.firstWindow(); + }) + + test.afterEach(async() => { + await electronApp.close(); + }) + + test('has correct links', async () => { + await expect( await window.getByRole("link", {name: "Get Support"}).getAttribute("href")).toBe("http://support.tidepool.org/") + await expect( await window.getByRole("link", {name: "Privacy and Terms of Use"}).getAttribute("href")).toBe("http://tidepool.org/legal/") + }) + + test.only('hovered links have correct colors', async () => { + const getSupportLink = window.locator('a').getByText('Get Support') + const privacyLink = window.locator('a').getByText('Privacy and Terms of Use') + const colorBefore = await getSupportLink.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + expect(colorBefore).toBe('rgb(151, 151, 151)') + + await getSupportLink.hover() + const color = await getSupportLink.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + expect(color).toBe('rgb(98, 124, 255)') + + // privacy link + const colorBefore2 = await privacyLink.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + expect(colorBefore2).toBe('rgb(151, 151, 151)') + await privacyLink.hover() + const color2 = await privacyLink.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + + expect(color2).toBe('rgb(98, 124, 255)') + + // await window.waitForSelector('i:has-text("Sign up")'); + // const signUpLink = window.locator('i').withText(/Sign up/); + // await signUpLink.screenshot({path: 'signup.png'}) + await window.waitForLoadState("load"); + + // await window.getByText("Loading...").waitFor({state: "hidden"}) + const html = await window.$("body") + const innerHtml = await html.innerHTML() + + await window.waitForTimeout(4000) + + const htmlAfter = await window.$("body") + console.log(await htmlAfter.innerHTML()) + console.log(await htmlAfter.innerHTML() == innerHtml) + // await window.waitForSelector('i:has-text("Sign up")') + console.log(await window.locator('a').all()) + }) + + test('has correct title', async () => { + await expect(await window.title()).toBe('Tidepool Uploader') + }) + + test('has correct buttons', async () => { + const signUpLink = window.locator('i').getByText(/Sign up/) + + const getSupportLink = window.locator('a').getByText('Get Support') + const privacyLink = window.locator('a').getByText('Privacy and Terms of Use') + + + + await window.waitForTimeout(4000) + await getSupportLink.screenshot({path: 'signup.png'}) + console.log('asdasdasd') + // await expect(await signUpLink).toBeVisible() + // await expect().toBeAttached() + + }) +}) diff --git a/yarn.lock b/yarn.lock index 8af98ea4d..a71a220dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2324,6 +2324,13 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@playwright/test@^1.42.1": + version "1.42.1" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.42.1.tgz#9eff7417bcaa770e9e9a00439e078284b301f31c" + integrity sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ== + dependencies: + playwright "1.42.1" + "@polka/url@^1.0.0-next.24": version "1.0.0-next.24" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" @@ -2609,6 +2616,13 @@ dependencies: undici-types "~5.26.4" +"@types/node@^20.11.30": + version "20.11.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.30.tgz#9c33467fc23167a347e73834f788f4b9f399d66f" + integrity sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" @@ -7414,6 +7428,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@2.3.2, fsevents@^2.1.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + fsevents@^1.2.7: version "1.2.13" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" @@ -7422,11 +7441,6 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - ftdi-js@0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/ftdi-js/-/ftdi-js-0.4.1.tgz#ba0f7f970908bac9a2d39deec99fa44e7dbd63ec" @@ -11809,7 +11823,21 @@ pl2303@0.1.0: dependencies: usb "2.11.0" -plist@3.1.0, plist@^3.0.4, plist@^3.0.5: +playwright-core@1.42.1: + version "1.42.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.42.1.tgz#13c150b93c940a3280ab1d3fbc945bc855c9459e" + integrity sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA== + +playwright@1.42.1: + version "1.42.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.42.1.tgz#79c828b51fe3830211137550542426111dc8239f" + integrity sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg== + dependencies: + playwright-core "1.42.1" + optionalDependencies: + fsevents "2.3.2" + +plist@3.1.0, plist@^3.0.5: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== @@ -11818,6 +11846,14 @@ plist@3.1.0, plist@^3.0.4, plist@^3.0.5: base64-js "^1.5.1" xmlbuilder "^15.1.1" +plist@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.6.tgz#7cfb68a856a7834bca6dbfe3218eb9c7740145d3" + integrity sha512-WiIVYyrp8TD4w8yCvyeIr+lkmrGRd5u0VbRnU+tP/aRLxP/YadJUYOMZJ/6hIa3oUyVCsycXvtNRgd5XBJIbiA== + dependencies: + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + plugin-error@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" From 26a889da0cd68147a1efabdb49a9c902df78218e Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Tue, 2 Apr 2024 13:54:05 +0200 Subject: [PATCH 02/15] chore: add test-e2e to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 45c17016b..c497ac1cc 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "serve-docs": "./node_modules/.bin/gitbook serve", "test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 jest", "test-all": "npm run lint && npm run test && npm run build", + "test-e2e": "playwright test electron.test.js", "lint": "node ./node_modules/eslint/bin/eslint.js --cache --format=node_modules/eslint-formatter-pretty .", "lint-fix": "npm run lint -- --fix", "build-main": "yarn build-main-quiet --progress --profile --colors", From fd8223fe37e14d2be434cfc9bf8d1d66550aba15 Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Tue, 2 Apr 2024 13:51:02 +0200 Subject: [PATCH 03/15] refactor: refactor + tests in electron --- .gitignore | 1 + playwright.config.js | 2 + test/e2e.js | 0 test/e2e/.prettierrc | 3 -- test/e2e/electron.test.js | 97 +++++++++++++++----------------------- test/e2e/utils/electron.js | 16 +++++++ test/example.js | 9 ---- 7 files changed, 58 insertions(+), 70 deletions(-) delete mode 100755 test/e2e.js delete mode 100644 test/e2e/.prettierrc create mode 100644 test/e2e/utils/electron.js delete mode 100755 test/example.js diff --git a/.gitignore b/.gitignore index 4f3e09c7e..14812d058 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ web/ /playwright-report/ /blob-report/ /playwright/.cache/ +videos \ No newline at end of file diff --git a/playwright.config.js b/playwright.config.js index 375882d15..9219aec5e 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -18,6 +18,7 @@ module.exports = defineConfig({ expect: { timeout: 5000 }, + fullyParallel: false, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, @@ -31,6 +32,7 @@ module.exports = defineConfig({ use: { /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + video: 'on-first-retry' }, projects: [ { diff --git a/test/e2e.js b/test/e2e.js deleted file mode 100755 index e69de29bb..000000000 diff --git a/test/e2e/.prettierrc b/test/e2e/.prettierrc deleted file mode 100644 index 1e50ff1c8..000000000 --- a/test/e2e/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "tabWidth": 2 -} \ No newline at end of file diff --git a/test/e2e/electron.test.js b/test/e2e/electron.test.js index cb40fd667..f6191af5b 100644 --- a/test/e2e/electron.test.js +++ b/test/e2e/electron.test.js @@ -1,4 +1,5 @@ -const { test, _electron: electron, expect } = require('@playwright/test') +const { test, expect, } = require('@playwright/test'); +const { startElectron } = require('./utils/electron'); // @ts-check test.describe('Home screen', () => { @@ -6,16 +7,10 @@ test.describe('Home screen', () => { let window; /** @type {import('@playwright/test').ElectronApplication} */ let electronApp; - test.beforeEach(async() => { - electronApp = await electron.launch({ args: ['./app/main.prod.js'] }); - const appPath = await electronApp.evaluate(async ({ app }) => { - // This runs in the main Electron process, parameter here is always - // the result of the require('electron') in the main app script. - return app.getAppPath(); - }); - console.log(appPath); - // Get the first window that the app opens, wait if necessary. + // HOOKS + test.beforeEach(async() => { + electronApp = await startElectron(); window = await electronApp.firstWindow(); }) @@ -23,68 +18,54 @@ test.describe('Home screen', () => { await electronApp.close(); }) + // TESTS test('has correct links', async () => { await expect( await window.getByRole("link", {name: "Get Support"}).getAttribute("href")).toBe("http://support.tidepool.org/") await expect( await window.getByRole("link", {name: "Privacy and Terms of Use"}).getAttribute("href")).toBe("http://tidepool.org/legal/") }) - test.only('hovered links have correct colors', async () => { - const getSupportLink = window.locator('a').getByText('Get Support') - const privacyLink = window.locator('a').getByText('Privacy and Terms of Use') - const colorBefore = await getSupportLink.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - expect(colorBefore).toBe('rgb(151, 151, 151)') - - await getSupportLink.hover() - const color = await getSupportLink.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - expect(color).toBe('rgb(98, 124, 255)') - - // privacy link - const colorBefore2 = await privacyLink.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - expect(colorBefore2).toBe('rgb(151, 151, 151)') - await privacyLink.hover() - const color2 = await privacyLink.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - - expect(color2).toBe('rgb(98, 124, 255)') - - // await window.waitForSelector('i:has-text("Sign up")'); - // const signUpLink = window.locator('i').withText(/Sign up/); - // await signUpLink.screenshot({path: 'signup.png'}) - await window.waitForLoadState("load"); - - // await window.getByText("Loading...").waitFor({state: "hidden"}) - const html = await window.$("body") - const innerHtml = await html.innerHTML() + test('hovered links have correct colors', async () => { + const links = ['Get Support', 'Privacy and Terms of Use'] - await window.waitForTimeout(4000) - - const htmlAfter = await window.$("body") - console.log(await htmlAfter.innerHTML()) - console.log(await htmlAfter.innerHTML() == innerHtml) - // await window.waitForSelector('i:has-text("Sign up")') - console.log(await window.locator('a').all()) + for (let linkText of links) { + const linkElement = window.locator('a').getByText(linkText) + const colorBefore = await linkElement.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + expect(colorBefore).toBe('rgb(151, 151, 151)') + + await linkElement.hover() + const color = await linkElement.evaluate((e) => { + return window.getComputedStyle(e).getPropertyValue("color")}) + expect(color).toBe('rgb(98, 124, 255)') + } }) test('has correct title', async () => { await expect(await window.title()).toBe('Tidepool Uploader') }) - test('has correct buttons', async () => { - const signUpLink = window.locator('i').getByText(/Sign up/) - - const getSupportLink = window.locator('a').getByText('Get Support') - const privacyLink = window.locator('a').getByText('Privacy and Terms of Use') + test("clicking on Log in button opens the login screen", async () => { + await window.waitForSelector("body"); + window.on("domcontentloaded", async () => { + return console.log("loaded") + }) + // 1. Here, I am able to check the innerHTML of the div#app element + const html = await window.$("div#app") + console.log(await html.innerHTML()) - + console.log("🔵 1") + await window.waitForLoadState("domcontentloaded") + console.log("🔵 2 - before timeout") await window.waitForTimeout(4000) - await getSupportLink.screenshot({path: 'signup.png'}) - console.log('asdasdasd') - // await expect(await signUpLink).toBeVisible() - // await expect().toBeAttached() - + // console.log("🔵 3") + // window = await electronApp.firstWindow(); + console.log("🔵 4 - after timeout") + + // When I try to check same element again - the innerHTML of the div#app element after the timeout, it throws an error that the element is not found + // I tried `body` as well, but with the same result + const html1 = await window.$("div#app") + console.log("🔵 5") + console.log(html1) }) }) diff --git a/test/e2e/utils/electron.js b/test/e2e/utils/electron.js new file mode 100644 index 000000000..d19d8fe39 --- /dev/null +++ b/test/e2e/utils/electron.js @@ -0,0 +1,16 @@ + +const { _electron: electron, } = require('@playwright/test') + +/** + * Launches Electron using Playwright. + * @returns {Promise} The Electron application instance. + */ +async function startElectron () { + return await electron.launch({ + args: ['./app/main.prod.js'], + + // recordVideo: { dir: './videos' }, + }); +} + +exports.startElectron = startElectron; \ No newline at end of file diff --git a/test/example.js b/test/example.js deleted file mode 100755 index c42692573..000000000 --- a/test/example.js +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable func-names */ -import { expect } from 'chai'; - - -describe('description', () => { - test('should have description', () => { - expect(1 + 2).to.equal(3); - }); -}); From 0384539eaa54833656b8fda44c2132937d72dc3d Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Tue, 2 Apr 2024 13:54:05 +0200 Subject: [PATCH 04/15] chore: add test-e2e to package.json From 7a47d73b2336a60a5410037e5361b56c7e6db5ea Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Tue, 2 Apr 2024 13:58:58 +0200 Subject: [PATCH 05/15] fix: remove some unused files --- .github/workflows/playwright.yml | 27 ------------ app/main.dev.js | 10 ++--- playwright-report/index.html | 68 ------------------------------- signup.png | Bin 42027 -> 0 bytes 4 files changed, 5 insertions(+), 100 deletions(-) delete mode 100644 .github/workflows/playwright.yml delete mode 100644 playwright-report/index.html delete mode 100644 signup.png diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index f7f7c1c41..000000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Playwright Tests -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - name: Install dependencies - run: npm install -g yarn && yarn - - name: Install Playwright Browsers - run: yarn playwright install --with-deps - - name: Run Playwright tests - run: yarn playwright test - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/app/main.dev.js b/app/main.dev.js index d0b163669..b3efbef5d 100755 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -76,11 +76,11 @@ if (process.env.NODE_ENV === 'development') { require('module').globalPaths.push(p); // eslint-disable-line process.env.APPIMAGE = path.join(__dirname, 'release'); autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml'); - // Object.defineProperty(app, 'isPackaged', { - // get() { - // return true; - // } - // }); + Object.defineProperty(app, 'isPackaged', { + get() { + return true; + } + }); } app.on('window-all-closed', () => { diff --git a/playwright-report/index.html b/playwright-report/index.html deleted file mode 100644 index b6cd673a1..000000000 --- a/playwright-report/index.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - Playwright Test Report - - - - -
- - - \ No newline at end of file diff --git a/signup.png b/signup.png deleted file mode 100644 index 3038b15d37ddb1c7d0d954ea83da8c21b0cbe70f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42027 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 From 199a79a65dcc1e8ce780e8bc28216bbae9ea5ac0 Mon Sep 17 00:00:00 2001 From: Chris McGee Date: Wed, 3 Apr 2024 20:53:29 -0400 Subject: [PATCH 06/15] avoid hash-patching mechanism --- app/main.dev.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/main.dev.js b/app/main.dev.js index b3efbef5d..31a02b1c1 100755 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -277,7 +277,7 @@ operating system, as soon as possible.`, let selectedPort; for (let i = 0; i < serialPortFilter.length; i++) { - selectedPort = portList.find((element) => + selectedPort = portList.find((element) => serialPortFilter[i].usbVendorId === parseInt(element.vendorId, 10) && serialPortFilter[i].usbProductId === parseInt(element.productId, 10) ); @@ -691,11 +691,14 @@ const handleIncomingUrl = (url) => { if (requestURL.pathname.includes('keycloak-redirect') || requestURL.pathname.includes('upload-redirect')) { if(mainWindow){ const { webContents } = mainWindow; - const requestHash = requestURL.hash; - const newUrl = `${baseURL}${requestHash}`; - if(webContents.getURL() !== newUrl){ - webContents.loadURL(newUrl); - } + // redirecting from the app html to app html with hash breaks devtools + // just send and append the hash if we're already in the app html + // if (webContents.getURL().includes(baseURL)) { + // webContents.send('newHash', requestHash); + // } else { + const requestHash = requestURL.hash; + webContents.loadURL(`${baseURL}${requestHash}`); + // } return; } @@ -717,7 +720,7 @@ if (!gotTheLock) { return handleIncomingUrl(url); } }); - + // Protocol handler for osx app.on('open-url', (event, url) => { event.preventDefault(); From ac2dedb14a27177f2261d0a9e416ae053bec557a Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Wed, 17 Jul 2024 11:31:23 +0200 Subject: [PATCH 07/15] fix: eslint --- test/e2e/electron.test.js | 70 ++++++++++++++++++++------------------ test/e2e/utils/electron.js | 2 +- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/test/e2e/electron.test.js b/test/e2e/electron.test.js index f6191af5b..1b18b6ac9 100644 --- a/test/e2e/electron.test.js +++ b/test/e2e/electron.test.js @@ -12,60 +12,64 @@ test.describe('Home screen', () => { test.beforeEach(async() => { electronApp = await startElectron(); window = await electronApp.firstWindow(); - }) + }); test.afterEach(async() => { await electronApp.close(); - }) + }); // TESTS test('has correct links', async () => { - await expect( await window.getByRole("link", {name: "Get Support"}).getAttribute("href")).toBe("http://support.tidepool.org/") - await expect( await window.getByRole("link", {name: "Privacy and Terms of Use"}).getAttribute("href")).toBe("http://tidepool.org/legal/") - }) + await expect( await window.getByRole('link', {name: 'Get Support'}).getAttribute('href')) + .toBe('http://support.tidepool.org/'); + await expect( await window.getByRole('link', {name: 'Privacy and Terms of Use'}) + .getAttribute('href')).toBe('http://tidepool.org/legal/'); + }); test('hovered links have correct colors', async () => { - const links = ['Get Support', 'Privacy and Terms of Use'] + const links = ['Get Support', 'Privacy and Terms of Use']; for (let linkText of links) { - const linkElement = window.locator('a').getByText(linkText) + const linkElement = window.locator('a').getByText(linkText); const colorBefore = await linkElement.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - expect(colorBefore).toBe('rgb(151, 151, 151)') + return window.getComputedStyle(e).getPropertyValue('color');}); + expect(colorBefore).toBe('rgb(151, 151, 151)'); - await linkElement.hover() + await linkElement.hover(); const color = await linkElement.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue("color")}) - expect(color).toBe('rgb(98, 124, 255)') + return window.getComputedStyle(e).getPropertyValue('color');}); + expect(color).toBe('rgb(98, 124, 255)'); } - }) + }); test('has correct title', async () => { - await expect(await window.title()).toBe('Tidepool Uploader') - }) + await expect(await window.title()).toBe('Tidepool Uploader'); + }); - test("clicking on Log in button opens the login screen", async () => { - await window.waitForSelector("body"); - window.on("domcontentloaded", async () => { - return console.log("loaded") - }) + test('clicking on Log in button opens the login screen', async () => { + await window.waitForSelector('body'); + window.on('domcontentloaded', async () => { + return console.log('loaded'); + }); // 1. Here, I am able to check the innerHTML of the div#app element - const html = await window.$("div#app") - console.log(await html.innerHTML()) + const html = await window.$('div#app'); + console.log(await html.innerHTML()); - console.log("🔵 1") - await window.waitForLoadState("domcontentloaded") - console.log("🔵 2 - before timeout") - await window.waitForTimeout(4000) + console.log('🔵 1'); + await window.waitForLoadState('domcontentloaded'); + console.log('🔵 2 - before timeout'); + await window.waitForTimeout(4000); // console.log("🔵 3") // window = await electronApp.firstWindow(); - console.log("🔵 4 - after timeout") + console.log('🔵 4 - after timeout'); - // When I try to check same element again - the innerHTML of the div#app element after the timeout, it throws an error that the element is not found + // When I try to check same element again - + // the innerHTML of the div#app element after the timeout, + // it throws an error that the element is not found // I tried `body` as well, but with the same result - const html1 = await window.$("div#app") - console.log("🔵 5") - console.log(html1) - }) -}) + const html1 = await window.$('div#app'); + console.log('🔵 5'); + console.log(html1); + }); +}); diff --git a/test/e2e/utils/electron.js b/test/e2e/utils/electron.js index d19d8fe39..4b125ec92 100644 --- a/test/e2e/utils/electron.js +++ b/test/e2e/utils/electron.js @@ -1,5 +1,5 @@ -const { _electron: electron, } = require('@playwright/test') +const { _electron: electron, } = require('@playwright/test'); /** * Launches Electron using Playwright. From 0cdabf345eb9f611b6594a043019c9822a92dcf6 Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Wed, 17 Jul 2024 17:26:29 +0200 Subject: [PATCH 08/15] test: login into electron application --- .env.example | 11 +++++ app/components/Login.js | 22 ++++++--- app/main.dev.js | 6 +++ app/preload.js | 12 +++++ package.json | 1 + playwright.config.js | 4 +- test/e2e/electron.test.js | 91 +++++++++++++++++++++++--------------- test/e2e/utils/electron.js | 3 +- yarn.lock | 5 +++ 9 files changed, 110 insertions(+), 45 deletions(-) create mode 100755 .env.example create mode 100644 app/preload.js diff --git a/.env.example b/.env.example new file mode 100755 index 000000000..4e46bcafe --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +#ROLLBAR_POST_TOKEN= + +API_URL=http://localhost:3000 +UPLOAD_URL=http://localhost:3000 +DATA_URL=http://localhost:3000 +BLIP_URL=http://localhost:3000 +DEBUG_ERROR=true +REDUX_LOG=false +REDUX_DEV_UI=false +E2E_USER_EMAIL= +E2E_USER_PASSWORD= \ No newline at end of file diff --git a/app/components/Login.js b/app/components/Login.js index 0e691961a..59347983d 100644 --- a/app/components/Login.js +++ b/app/components/Login.js @@ -15,7 +15,7 @@ * == BSD2 LICENSE == */ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import styles from '../../styles/components/Login.module.less'; import { useDispatch, useSelector } from 'react-redux'; import env from '../utils/env'; @@ -30,16 +30,23 @@ export const Login = () => { const dispatch = useDispatch(); const disabled = useSelector((state) => Boolean(state.unsupported)); const errorMessage = useSelector((state) => state.loginErrorMessage); - const forgotPasswordUrl = useSelector( - (state) => state.blipUrls.forgotPassword - ); - const isLoggingIn = useSelector( - (state) => state.working.loggingIn.inProgress - ); + const forgotPasswordUrl = useSelector((state) => state.blipUrls.forgotPassword); + const isLoggingIn = useSelector((state) => state.working.loggingIn.inProgress); const keycloakConfig = useSelector((state) => state.keycloakConfig); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [remember, setRemember] = useState(true); + const [loginUrl, setLoginUrl] = useState(''); + + useEffect(() => { + if (keycloakConfig.initialized) { + try { + setLoginUrl(keycloak.createLoginUrl()); + } catch (error) { + console.error('Error creating Keycloak login URL:', error); + } + } + }, [keycloakConfig]); if(auth?.user){ auth.events._raiseUserSignedIn(auth.user); @@ -70,6 +77,7 @@ export const Login = () => { className={styles.button} onClick={handleLogin} disabled={isLoggingIn || disabled || auth?.isLoading} + data-testUrl={loginUrl ? loginUrl.toString() : ''} > {text} diff --git a/app/main.dev.js b/app/main.dev.js index 31a02b1c1..a08c2d85c 100755 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -152,6 +152,7 @@ function createWindow() { height: 769, resizable: resizable, webPreferences: { + preload: path.join(__dirname, 'preload.js'), nodeIntegration: true, contextIsolation: false, // so that we can access process from app.html }, @@ -705,6 +706,11 @@ const handleIncomingUrl = (url) => { } }; +ipcMain.handle('handle-incoming-url', async (event, url) => { + console.log('handle-incoming-url called with URL:', url); + handleIncomingUrl(url); +}); + const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { diff --git a/app/preload.js b/app/preload.js new file mode 100644 index 000000000..3d47e1608 --- /dev/null +++ b/app/preload.js @@ -0,0 +1,12 @@ +const { ipcRenderer } = require('electron'); + +console.log('Preload script is running'); + +window.electron = { + handleIncomingUrl: (url) => { + console.log('handleIncomingUrl called with URL:', url); + return ipcRenderer.invoke('handle-incoming-url', url); + } +}; + +console.log('Exposed handleIncomingUrl method to window.electron'); \ No newline at end of file diff --git a/package.json b/package.json index c497ac1cc..740646b21 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,7 @@ "cross-env": "7.0.3", "css-loader": "5.2.7", "difflet": "1.0.1", + "dotenv": "^16.4.5", "drivelist": "11.1.0", "electron": "27.3.0", "electron-builder": "24.9.1", diff --git a/playwright.config.js b/playwright.config.js index 9219aec5e..9ccd319a1 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -14,9 +14,9 @@ module.exports = defineConfig({ testDir: './test/e2e', /* Run tests in files in parallel */ // globalTimeout: 10000, - timeout: 15000, + timeout: 60000, expect: { - timeout: 5000 + timeout: 20000 }, fullyParallel: false, diff --git a/test/e2e/electron.test.js b/test/e2e/electron.test.js index 1b18b6ac9..97abc0a17 100644 --- a/test/e2e/electron.test.js +++ b/test/e2e/electron.test.js @@ -1,28 +1,31 @@ -const { test, expect, } = require('@playwright/test'); +const { test, expect, chromium } = require('@playwright/test'); const { startElectron } = require('./utils/electron'); - +require('dotenv').config(); // @ts-check test.describe('Home screen', () => { /** @type {import('@playwright/test').Page} */ - let window; + let window; /** @type {import('@playwright/test').ElectronApplication} */ let electronApp; - + + /** @type {import('@playwright/test').ChromiumBrowser} */ + let browser; + // HOOKS - test.beforeEach(async() => { + test.beforeEach(async () => { electronApp = await startElectron(); window = await electronApp.firstWindow(); }); - test.afterEach(async() => { + test.afterEach(async () => { await electronApp.close(); }); // TESTS test('has correct links', async () => { - await expect( await window.getByRole('link', {name: 'Get Support'}).getAttribute('href')) + expect(await window.getByRole('link', { name: 'Get Support' }).getAttribute('href')) .toBe('http://support.tidepool.org/'); - await expect( await window.getByRole('link', {name: 'Privacy and Terms of Use'}) + expect(await window.getByRole('link', { name: 'Privacy and Terms of Use' }) .getAttribute('href')).toBe('http://tidepool.org/legal/'); }); @@ -32,44 +35,62 @@ test.describe('Home screen', () => { for (let linkText of links) { const linkElement = window.locator('a').getByText(linkText); const colorBefore = await linkElement.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue('color');}); + return window.getComputedStyle(e).getPropertyValue('color'); + }); expect(colorBefore).toBe('rgb(151, 151, 151)'); - + await linkElement.hover(); const color = await linkElement.evaluate((e) => { - return window.getComputedStyle(e).getPropertyValue('color');}); + return window.getComputedStyle(e).getPropertyValue('color'); + }); expect(color).toBe('rgb(98, 124, 255)'); } }); test('has correct title', async () => { - await expect(await window.title()).toBe('Tidepool Uploader'); + expect(await window.title()).toBe('Tidepool Uploader'); }); - test('clicking on Log in button opens the login screen', async () => { - await window.waitForSelector('body'); - window.on('domcontentloaded', async () => { - return console.log('loaded'); - }); - - // 1. Here, I am able to check the innerHTML of the div#app element - const html = await window.$('div#app'); - console.log(await html.innerHTML()); + test('can login with patient account', async () => { + let url; + await new Promise(async (resolve) => { + await window.waitForSelector('body'); + await window.waitForLoadState('domcontentloaded'); - console.log('🔵 1'); - await window.waitForLoadState('domcontentloaded'); - console.log('🔵 2 - before timeout'); - await window.waitForTimeout(4000); - // console.log("🔵 3") - // window = await electronApp.firstWindow(); - console.log('🔵 4 - after timeout'); + const login = window.getByRole('button', { name: 'Log in' }); + url = await login.getAttribute('data-testurl'); + console.log('[Electron][Auth URL] ', url); + electronApp.close(); + resolve(); + }).then(async () => { + browser = await chromium.launch(); + console.log('[Chromium] Started 🎉'); + const page = await browser.newPage(); + await page.goto(url); + await page.getByPlaceholder('Email').waitFor('visible', { timeout: 10000 }); + await page.getByPlaceholder('Email').fill(process.env.E2E_USER_EMAIL); + await page.getByRole('button', { name: 'Next' }).click(); + await page.getByPlaceholder('Password').waitFor('visible', { timeout: 10000 }); + await page.getByPlaceholder('Password').fill('tidepool'); + await page.getByRole('button', { name: 'Log In' }).click(); + + console.log('[Chromium] Clicked Log In button'); + console.log('[Chromium] Waiting for the next page'); + const href = await page.getByRole('link', { name: 'Launch Uploader' }).getAttribute('href'); + console.log(href); + await browser.close(); - // When I try to check same element again - - // the innerHTML of the div#app element after the timeout, - // it throws an error that the element is not found - // I tried `body` as well, but with the same result - const html1 = await window.$('div#app'); - console.log('🔵 5'); - console.log(html1); + return href; + }).then(async (href) => { + + electronApp = await startElectron(); + window = await electronApp.firstWindow(); + + await window.waitForLoadState('domcontentloaded'); + await window.evaluate((url) => { + window.electron.handleIncomingUrl(url); + }, href); + await expect(window.getByRole('heading', { name: 'Choose devices' })).toBeVisible(); + }); }); }); diff --git a/test/e2e/utils/electron.js b/test/e2e/utils/electron.js index 4b125ec92..28bb28b7e 100644 --- a/test/e2e/utils/electron.js +++ b/test/e2e/utils/electron.js @@ -3,7 +3,8 @@ const { _electron: electron, } = require('@playwright/test'); /** * Launches Electron using Playwright. - * @returns {Promise} The Electron application instance. + * @returns {Promise} + The Electron application instance. */ async function startElectron () { return await electron.launch({ diff --git a/yarn.lock b/yarn.lock index a71a220dd..8cf8eb808 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5960,6 +5960,11 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + dotenv@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-9.0.2.tgz#dacc20160935a37dea6364aa1bef819fb9b6ab05" From ebce8f74051a5ab331514b8c57c3a35235730baa Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 13:46:42 +0200 Subject: [PATCH 09/15] ci: update ci to run test for each commit --- .circleci/config.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ef0c6bcc..53e904224 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,6 +2,33 @@ version: 2.1 orbs: node: circleci/node@5.2.0 jobs: + test-e2e: + working_directory: ~/tidepool-org/chrome-uploader + parallelism: 1 + environment: + BASH_ENV: ".circleci/bash_env.sh" + DISPLAY: ":99" + docker: + - image: cimg/node:18.17.1-browsers + steps: + - setup_remote_docker: + version: docker23 + - run: sudo apt-get update && sudo apt-get install -y build-essential git curl libusb-1.0 libavutil-dev libxss1 libsecret-1-dev libudev-dev libgtk-3-0 libcanberra-gtk3-module packagekit-gtk3-module chromium-browser fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatspi2.0-0 libcairo2 libcups2 libgbm1 libgdk-pixbuf2.0-0 libgtk-3-0 libpango-1.0-0 libpangocairo-1.0-0 libxcursor1 libxss1 xdg-utils xvfb libdbus-glib-1-2 libgtk-3-dev libxt6 + - checkout + - run: git submodule sync + - run: git submodule update --init + - run: echo 'export PATH=${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin' >> $BASH_ENV + - restore_cache: + key: dependency-cache-web-{{ checksum "package.json" }} + - run: yarn config set cache-folder ~/.cache/yarn + - run: yarn --frozen-lockfile + - save_cache: + key: dependency-cache-web-{{ checksum "package.json" }} + paths: + - ~/.cache/yarn + - ./node_modules + # Run end-to-end tests + - run: Xvfb :99 -screen 0 1280x1024x24 & > /dev/null && yarn test-e2e build-macos: resource_class: macos.m1.medium.gen1 working_directory: ~/tidepool-org/chrome-uploader @@ -186,3 +213,4 @@ workflows: filters: tags: only: /^v.*/ + - test-e2e From 4c102f09b61468f1e02df6789cd74bf77a75458b Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 13:49:07 +0200 Subject: [PATCH 10/15] revert: revert changes - doesn't needed to add custom attribute --- app/components/Login.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/app/components/Login.js b/app/components/Login.js index 59347983d..533b59a75 100644 --- a/app/components/Login.js +++ b/app/components/Login.js @@ -15,7 +15,7 @@ * == BSD2 LICENSE == */ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import styles from '../../styles/components/Login.module.less'; import { useDispatch, useSelector } from 'react-redux'; import env from '../utils/env'; @@ -30,23 +30,16 @@ export const Login = () => { const dispatch = useDispatch(); const disabled = useSelector((state) => Boolean(state.unsupported)); const errorMessage = useSelector((state) => state.loginErrorMessage); - const forgotPasswordUrl = useSelector((state) => state.blipUrls.forgotPassword); - const isLoggingIn = useSelector((state) => state.working.loggingIn.inProgress); + const forgotPasswordUrl = useSelector( + (state) => state.blipUrls.forgotPassword + ); + const isLoggingIn = useSelector( + (state) => state.working.loggingIn.inProgress + ); const keycloakConfig = useSelector((state) => state.keycloakConfig); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [remember, setRemember] = useState(true); - const [loginUrl, setLoginUrl] = useState(''); - - useEffect(() => { - if (keycloakConfig.initialized) { - try { - setLoginUrl(keycloak.createLoginUrl()); - } catch (error) { - console.error('Error creating Keycloak login URL:', error); - } - } - }, [keycloakConfig]); if(auth?.user){ auth.events._raiseUserSignedIn(auth.user); @@ -77,7 +70,6 @@ export const Login = () => { className={styles.button} onClick={handleLogin} disabled={isLoggingIn || disabled || auth?.isLoading} - data-testUrl={loginUrl ? loginUrl.toString() : ''} > {text} @@ -150,4 +142,4 @@ export const Login = () => { ); }; -export default Login; +export default Login; \ No newline at end of file From d12c3463c742180b660f4965ca63905719c7258d Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 13:49:32 +0200 Subject: [PATCH 11/15] fix: update for in-browser-merged branch --- test/e2e/electron.test.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/e2e/electron.test.js b/test/e2e/electron.test.js index 97abc0a17..005c3d632 100644 --- a/test/e2e/electron.test.js +++ b/test/e2e/electron.test.js @@ -23,9 +23,9 @@ test.describe('Home screen', () => { // TESTS test('has correct links', async () => { - expect(await window.getByRole('link', { name: 'Get Support' }).getAttribute('href')) + expect(await window.getByRole('link', { name: 'Get Support' }).getAttribute('href')) .toBe('http://support.tidepool.org/'); - expect(await window.getByRole('link', { name: 'Privacy and Terms of Use' }) + expect(await window.getByRole('link', { name: 'Privacy and Terms of Use' }) .getAttribute('href')).toBe('http://tidepool.org/legal/'); }); @@ -57,8 +57,12 @@ test.describe('Home screen', () => { await window.waitForSelector('body'); await window.waitForLoadState('domcontentloaded'); - const login = window.getByRole('button', { name: 'Log in' }); - url = await login.getAttribute('data-testurl'); + await window.getByRole('button', { name: 'Log in' }).click(); + + // eslint-disable-next-line max-len + const urlPattern = /\/realms\/qa2\/protocol\/openid-connect\/auth\?client_id=tidepool-uploader-sso/; + url = (await window.waitForRequest(request => urlPattern.test(request.url()))).url(); + console.log('[Electron][Auth URL] ', url); electronApp.close(); resolve(); From 6b2bc680b3100e0aefca309bf8dec6dedf20986f Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 14:01:25 +0200 Subject: [PATCH 12/15] ci: temporary disable tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 53e904224..0c846e659 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -213,4 +213,4 @@ workflows: filters: tags: only: /^v.*/ - - test-e2e + # - test-e2e From 79793da3a77c409b72ead8242970b3a410a27a7f Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 14:57:02 +0200 Subject: [PATCH 13/15] ci: testing new approach --- .circleci/config.yml | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0c846e659..979a9a037 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,14 +5,19 @@ jobs: test-e2e: working_directory: ~/tidepool-org/chrome-uploader parallelism: 1 + docker: + - image: cimg/node:18.17.1-browsers environment: BASH_ENV: ".circleci/bash_env.sh" DISPLAY: ":99" - docker: - - image: cimg/node:18.17.1-browsers steps: - - setup_remote_docker: - version: docker23 + - run: + name: Install nvm and node + command: | + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash + source ~/.nvm/nvm.sh + nvm install v18.17.1 + nvm alias default v18.17.1 - run: sudo apt-get update && sudo apt-get install -y build-essential git curl libusb-1.0 libavutil-dev libxss1 libsecret-1-dev libudev-dev libgtk-3-0 libcanberra-gtk3-module packagekit-gtk3-module chromium-browser fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatspi2.0-0 libcairo2 libcups2 libgbm1 libgdk-pixbuf2.0-0 libgtk-3-0 libpango-1.0-0 libpangocairo-1.0-0 libxcursor1 libxss1 xdg-utils xvfb libdbus-glib-1-2 libgtk-3-dev libxt6 - checkout - run: git submodule sync @@ -28,7 +33,19 @@ jobs: - ~/.cache/yarn - ./node_modules # Run end-to-end tests - - run: Xvfb :99 -screen 0 1280x1024x24 & > /dev/null && yarn test-e2e + - run: + name: Start Xvfb + command: | + if pgrep -x "Xvfb" > /dev/null; then + echo "Stopping existing Xvfb instance" + pkill -x Xvfb + fi + Xvfb :99 -screen 0 1280x1024x24 &> /dev/null & + - run: + name: Run E2E Tests + command: | + export DISPLAY=:99 + yarn test-e2e build-macos: resource_class: macos.m1.medium.gen1 working_directory: ~/tidepool-org/chrome-uploader @@ -205,12 +222,18 @@ workflows: filters: tags: only: /^v.*/ + requires: + - test-e2e - build-web: filters: tags: only: /^v.*/ + requires: + - test-e2e - build-windows: filters: tags: only: /^v.*/ - # - test-e2e + requires: + - test-e2e + - test-e2e From ccb48cae920221d498b418701e44af52b533c28b Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 15:15:58 +0200 Subject: [PATCH 14/15] ci: simplify ci by circleci article https://circleci.com/blog/electron-testing/ --- .circleci/config.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 979a9a037..dbf987f78 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,9 +7,6 @@ jobs: parallelism: 1 docker: - image: cimg/node:18.17.1-browsers - environment: - BASH_ENV: ".circleci/bash_env.sh" - DISPLAY: ":99" steps: - run: name: Install nvm and node @@ -18,7 +15,6 @@ jobs: source ~/.nvm/nvm.sh nvm install v18.17.1 nvm alias default v18.17.1 - - run: sudo apt-get update && sudo apt-get install -y build-essential git curl libusb-1.0 libavutil-dev libxss1 libsecret-1-dev libudev-dev libgtk-3-0 libcanberra-gtk3-module packagekit-gtk3-module chromium-browser fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatspi2.0-0 libcairo2 libcups2 libgbm1 libgdk-pixbuf2.0-0 libgtk-3-0 libpango-1.0-0 libpangocairo-1.0-0 libxcursor1 libxss1 xdg-utils xvfb libdbus-glib-1-2 libgtk-3-dev libxt6 - checkout - run: git submodule sync - run: git submodule update --init @@ -32,20 +28,9 @@ jobs: paths: - ~/.cache/yarn - ./node_modules - # Run end-to-end tests - - run: - name: Start Xvfb - command: | - if pgrep -x "Xvfb" > /dev/null; then - echo "Stopping existing Xvfb instance" - pkill -x Xvfb - fi - Xvfb :99 -screen 0 1280x1024x24 &> /dev/null & - run: name: Run E2E Tests - command: | - export DISPLAY=:99 - yarn test-e2e + command: yarn test-e2e build-macos: resource_class: macos.m1.medium.gen1 working_directory: ~/tidepool-org/chrome-uploader From ede636fc5c99bf111bbf497320811fcfe7d18983 Mon Sep 17 00:00:00 2001 From: Vojtech Cerveny Date: Thu, 18 Jul 2024 15:16:58 +0200 Subject: [PATCH 15/15] ci: fix - need to build app --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index dbf987f78..d92e27872 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,6 +28,7 @@ jobs: paths: - ~/.cache/yarn - ./node_modules + - run: yarn build - run: name: Run E2E Tests command: yarn test-e2e