From 6a4464370ba0e0c4475bdee9bfb79c0ebe61a5bf Mon Sep 17 00:00:00 2001 From: MiniDigger | Martin Date: Sat, 17 Feb 2024 17:07:00 +0100 Subject: [PATCH] test: cleanup e2e tests and add a github action --- .github/workflows/e2e.yml | 59 +++++++++++++++++++++++++++++++++ e2e/codecept.conf.ts | 45 ++++++++++++++----------- e2e/package.json | 5 +-- e2e/pnpm-lock.yaml | 48 +++++++-------------------- e2e/steps.d.ts | 8 ++--- e2e/tests/IndexPage.ts | 17 ++++++++++ e2e/tests/mainpage_test.ts | 11 ------ e2e/tsconfig.json | 1 + e2e/utils/IndexPage.ts | 15 ++------- e2e/utils/steps.ts | 10 ------ e2e/utils/util.ts | 17 ++++++++++ frontend/src/app.vue | 2 ++ frontend/src/types/globals.d.ts | 5 +++ 13 files changed, 146 insertions(+), 97 deletions(-) create mode 100644 .github/workflows/e2e.yml create mode 100644 e2e/tests/IndexPage.ts delete mode 100644 e2e/tests/mainpage_test.ts delete mode 100644 e2e/utils/steps.ts create mode 100644 e2e/utils/util.ts create mode 100644 frontend/src/types/globals.d.ts diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml new file mode 100644 index 000000000..6893c65f1 --- /dev/null +++ b/.github/workflows/e2e.yml @@ -0,0 +1,59 @@ +name: e2e + +on: + # allow manual dispatch + workflow_dispatch: + # run on pushes to staging + push: + branches: + - staging + - autoimport + +jobs: + e2e: + name: 'Hangar E2E Tests' + runs-on: ubuntu-latest + steps: + - name: BrowserStack Env Setup + uses: browserstack/github-actions/setup-env@master + with: + username: ${{ secrets.BROWSERSTACK_USERNAME }} + access-key: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set up pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Cache Pnpm + uses: actions/cache@v4 + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install e2e deps + env: + CI: true + run: (cd e2e && pnpm install --frozen-lockfile) + + - name: Run e2e tests + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + run: (cd e2e && pnpm run browserstack-multiple) diff --git a/e2e/codecept.conf.ts b/e2e/codecept.conf.ts index 940a471c7..c1aee2471 100644 --- a/e2e/codecept.conf.ts +++ b/e2e/codecept.conf.ts @@ -10,9 +10,26 @@ const DEV = process.env.BROWSERSTACK_DEV === "true"; const LOCAL = process.env.BROWSERSTACK_LOCAL === "true"; const BUILD_NAME = process.env.BROWSERSTACK_BUILD_NAME || "browserstack-build-1" + (LOCAL ? "-local" : ""); +console.table({ + DEBUG, + DEV, + LOCAL, + BUILD_NAME, +}); + +const defaultCapabilities = { + browserVersion: "latest", + projectName: "Hangar E2E", + buildName: BUILD_NAME, + "browserstack.debug": DEBUG ? "true" : undefined, + "browserstack.networkLogs": DEBUG ? "true" : undefined, + "browserstack.consoleLogs": DEBUG ? "info" : undefined, + "browserstack.local": LOCAL ? "true" : "false", +}; + exports.config = { name: "Hangar E2E", - tests: "./tests/*_test.ts", + tests: "./tests/*.ts", output: "./output", timeout: 120, @@ -23,13 +40,9 @@ exports.config = { key: DEV ? undefined : BROWSERSTACK_ACCESS_KEY, browser: "Edge", capabilities: { + ...defaultCapabilities, os: "Windows", osVersion: "11", - browserVersion: "latest", - projectName: "Hangar E2E", - buildName: BUILD_NAME, - "browserstack.debug": DEBUG ? "true" : undefined, - "browserstack.networkLogs": DEBUG ? "true" : undefined, }, }, }, @@ -40,25 +53,17 @@ exports.config = { { browser: "Safari", capabilities: { + ...defaultCapabilities, os: "OS X", osVersion: "Sonoma", - browserVersion: "latest", - projectName: "Hangar E2E", - buildName: BUILD_NAME, - "browserstack.debug": DEBUG ? "true" : undefined, - "browserstack.networkLogs": DEBUG ? "true" : undefined, }, }, { browser: "Edge", capabilities: { + ...defaultCapabilities, os: "Windows", osVersion: "11", - browserVersion: "latest", - projectName: "Hangar E2E", - buildName: BUILD_NAME, - "browserstack.debug": DEBUG ? "true" : undefined, - "browserstack.networkLogs": DEBUG ? "true" : undefined, }, }, ], @@ -83,7 +88,7 @@ exports.config = { teardown: LOCAL && !DEV - ? function () { + ? async function () { exports.bs_local.stop(() => { console.log("Disconnected Local"); }); @@ -91,8 +96,8 @@ exports.config = { : undefined, include: { - I: "./utils/steps.ts", - IndexPage: "./pages/IndexPage.ts", + util: "./utils/util.ts", + IndexPage: "./utils/IndexPage.ts", }, plugins: { @@ -107,4 +112,4 @@ exports.config = { enabled: true, }, }, -}; +} as CodeceptJS.MainConfig; diff --git a/e2e/package.json b/e2e/package.json index 62528126f..2937f8323 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,7 +1,6 @@ { "name": "hangar-e2e", "version": "1.0.0", - "description": "", "scripts": { "staging": "BROWSERSTACK_DEV=true npx codeceptjs run -c codecept.conf.ts --steps -p pauseOnFail", "local": "BROWSERSTACK_DEV=true BROWSERSTACK_LOCAL=true npx codeceptjs run -c codecept.conf.ts --steps -p pauseOnFail", @@ -9,12 +8,10 @@ "browserstack-multiple": "npx codeceptjs run-multiple bstack -c codecept.conf.ts --steps", "browserstack-local": "BROWSERSTACK_LOCAL=true npx codeceptjs run -c codecept.conf.ts --steps" }, - "author": "", - "license": "ISC", "dependencies": { "@types/chai": "4.3.11", "browserstack-local": "^1.5.2", - "chai": "5.1.0", + "chai": "4.4.1", "codeceptjs": "^3.2.3", "dotenv": "^16.0.0", "ts-node": "10.9.2", diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml index 74b48f78b..7fe6239ee 100644 --- a/e2e/pnpm-lock.yaml +++ b/e2e/pnpm-lock.yaml @@ -12,8 +12,8 @@ dependencies: specifier: ^1.5.2 version: 1.5.5 chai: - specifier: 5.1.0 - version: 5.1.0 + specifier: 4.4.1 + version: 4.4.1 codeceptjs: specifier: ^3.2.3 version: 3.5.12(typescript@5.3.3) @@ -873,11 +873,6 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: false - /assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - dev: false - /ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} @@ -1212,15 +1207,17 @@ packages: type-detect: 4.0.8 dev: false - /chai@5.1.0: - resolution: {integrity: sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==} - engines: {node: '>=12'} + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} dependencies: - assertion-error: 2.0.1 - check-error: 2.0.0 - deep-eql: 5.0.1 - loupe: 3.1.0 - pathval: 2.0.0 + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 dev: false /chainsaw@0.1.0: @@ -1261,11 +1258,6 @@ packages: get-func-name: 2.0.2 dev: false - /check-error@2.0.0: - resolution: {integrity: sha512-tjLAOBHKVxtPoHe/SA7kNOMvhCRdCJ3vETdeY0RuAc9popf+hyaSV6ZEg9hr4cpWF7jmo/JSWEnLDrnijS9Tog==} - engines: {node: '>= 16'} - dev: false - /checkit@0.7.0: resolution: {integrity: sha512-QgiWB8gMdF/CbmWyuxCk+f2MPQe0G1DfJfHCTbrfZlY3FnJWdnW+EGsRJctcYz/IrXxPYJmjRjdgmKUkyIZl/Q==} dependencies: @@ -1713,11 +1705,6 @@ packages: type-detect: 4.0.8 dev: false - /deep-eql@5.0.1: - resolution: {integrity: sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==} - engines: {node: '>=6'} - dev: false - /deep-keys@0.5.0: resolution: {integrity: sha512-/80a4+9lbLj1hRxG0ULtEOGtbM4hN/5u1Vu6kc6ZkYePUq+ZhtboRIsWTVKplc2ET1xY2FMVwhyt46w9vPf9Rg==} engines: {node: '>=0.10.0'} @@ -2895,12 +2882,6 @@ packages: get-func-name: 2.0.2 dev: false - /loupe@3.1.0: - resolution: {integrity: sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==} - dependencies: - get-func-name: 2.0.2 - dev: false - /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -3415,11 +3396,6 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: false - /pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} - engines: {node: '>= 14.16'} - dev: false - /pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} dependencies: diff --git a/e2e/steps.d.ts b/e2e/steps.d.ts index 6d0c84e49..7865add22 100644 --- a/e2e/steps.d.ts +++ b/e2e/steps.d.ts @@ -1,11 +1,11 @@ /// -type steps_file = typeof import('./utils/steps'); -type IndexPage = typeof import('./pages/IndexPage'); +type util = typeof import('./utils/util'); +type IndexPage = typeof import('./utils/IndexPage'); declare namespace CodeceptJS { - interface SupportObject { I: I, current: any, IndexPage: IndexPage } + interface SupportObject { I: I, current: any, util: util, IndexPage: IndexPage } interface Methods extends WebDriver {} - interface I extends ReturnType {} + interface I extends WithTranslation {} namespace Translation { interface Actions {} } diff --git a/e2e/tests/IndexPage.ts b/e2e/tests/IndexPage.ts new file mode 100644 index 000000000..c46c70936 --- /dev/null +++ b/e2e/tests/IndexPage.ts @@ -0,0 +1,17 @@ +import { expect } from "chai"; + +Feature("Index Page"); + +Scenario("Test Project List", async ({ I, util, IndexPage }) => { + util.openHangarPage("/"); + + let placeholder = await I.grabAttributeFrom(IndexPage.searchField, "placeholder"); + expect(placeholder).to.not.contain("0"); + + I.fillField(IndexPage.searchField, "Some Value That doesnt Exist"); + I.waitInUrl("query"); + placeholder = await I.grabAttributeFrom(IndexPage.searchField, "placeholder"); + expect(placeholder).to.contain("0"); + + await util.browserStackStatus(true, "Test passed"); +}); diff --git a/e2e/tests/mainpage_test.ts b/e2e/tests/mainpage_test.ts deleted file mode 100644 index a230c7d11..000000000 --- a/e2e/tests/mainpage_test.ts +++ /dev/null @@ -1,11 +0,0 @@ -//import { expect } from "chai"; - -Feature("Main Page"); - -Scenario("Test", async ({ I }) => { - I.openHangarPage("/"); - const placeholder = await I.grabAttributeFrom("input[name='query']", "placeholder"); - - //expect(placeholder).to.not.contain("1"); - // await I.executeScript('browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"passed","reason": "Product matched!"}}'); -}); diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 256b296f3..618440397 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -13,6 +13,7 @@ "strictNullChecks": false, "types": [ "codeceptjs", + "chai", "node" ], "declaration": true, diff --git a/e2e/utils/IndexPage.ts b/e2e/utils/IndexPage.ts index 77521798f..ecb016a2e 100644 --- a/e2e/utils/IndexPage.ts +++ b/e2e/utils/IndexPage.ts @@ -1,12 +1,3 @@ -const { I } = inject(); - -class IndexPage { - constructor() { - //insert your locators - // this.button = '#button' - } - // insert your methods here -} - -module.exports = new IndexPage(); -export = IndexPage; +module.exports = new (class { + searchField = "input[name='query']"; +})(); diff --git a/e2e/utils/steps.ts b/e2e/utils/steps.ts deleted file mode 100644 index ea923f2ba..000000000 --- a/e2e/utils/steps.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { actor } from "codeceptjs"; - -module.exports = function () { - const url = process.env.BROWSERSTACK_LOCAL === "true" ? "http://localhost:3333" : "https://hangar.papermc.dev"; - return actor({ - openHangarPage: function (path: string) { - this.amOnPage(url + path); - }, - }); -}; diff --git a/e2e/utils/util.ts b/e2e/utils/util.ts new file mode 100644 index 000000000..3f5212c91 --- /dev/null +++ b/e2e/utils/util.ts @@ -0,0 +1,17 @@ +const { I } = inject(); + +module.exports = new (class { + url = process.env.BROWSERSTACK_LOCAL === "true" ? "http://localhost:3333" : "https://hangar.papermc.dev"; + + public openHangarPage(path: string) { + I.amOnPage(this.url + path); + I.waitForFunction(() => window["hangarLoaded"], 10); + } + + public async browserStackStatus(passed: boolean, reason: string) { + if (process.env.BROWSERSTACK_DEV === "true") return; + await I.executeScript( + `browserstack_executor: {"action": "setSessionStatus", "arguments": {"status":"${passed ? "passed" : "failed"}","reason": "${reason}"}}`, + ); + } +})(); diff --git a/frontend/src/app.vue b/frontend/src/app.vue index f59c83bbe..dc70c0d05 100644 --- a/frontend/src/app.vue +++ b/frontend/src/app.vue @@ -17,6 +17,8 @@ settingsLog("render for user", authStore.user?.name, "with darkmode", settingsSt if (process.client) { document.documentElement.classList.remove("light", "dark"); document.documentElement.classList.add(settingsStore.darkMode ? "dark" : "light"); + + window.hangarLoaded = true; } useHead({ diff --git a/frontend/src/types/globals.d.ts b/frontend/src/types/globals.d.ts new file mode 100644 index 000000000..f752bb7d2 --- /dev/null +++ b/frontend/src/types/globals.d.ts @@ -0,0 +1,5 @@ +declare global { + interface Window { + hangarLoaded?: boolean; + } +}