From f7cccc97d83f7ac05720e9504063a5ff08049518 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:12:28 -0500 Subject: [PATCH 01/12] fix jwt service tests --- tests/jwt.service.test.ts | 234 ++++++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 88 deletions(-) diff --git a/tests/jwt.service.test.ts b/tests/jwt.service.test.ts index 6b3a30c2..6eee1779 100644 --- a/tests/jwt.service.test.ts +++ b/tests/jwt.service.test.ts @@ -2,14 +2,16 @@ import { describe, expect, test } from "vitest"; import { DefaultTokensValues } from "@/features/common/values/default-tokens.values"; import { validateJwtFormat } from "@/features/common/services/jwt.service"; import { JwtTypeValues } from "@/features/common/values/jwt-type.values"; +import { DebuggerTaskValues } from "@/features/common/values/debugger-task.values"; +import { DebuggerInputValues } from "@/features/common/values/debugger-input.values"; describe("validateJwtFormat", () => { - const tokenHS256 = DefaultTokensValues.hs256.token; - const tokenHS384 = DefaultTokensValues.hs384.token; - const tokenHS512 = DefaultTokensValues.hs512.token; - const tokenRS256 = DefaultTokensValues.rs256.token; - const tokenRS384 = DefaultTokensValues.rs384.token; - const tokenRS512 = DefaultTokensValues.rs512.token; + const tokenHS256 = DefaultTokensValues.HS256.token; + const tokenHS384 = DefaultTokensValues.HS384.token; + const tokenHS512 = DefaultTokensValues.HS512.token; + const tokenRS256 = DefaultTokensValues.RS256.token; + const tokenRS384 = DefaultTokensValues.RS384.token; + const tokenRS512 = DefaultTokensValues.RS512.token; const unsecured = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."; @@ -24,7 +26,7 @@ describe("validateJwtFormat", () => { const invalidToken8 = "eyJhbGciOiJIUzI1N9.dGVzdA.Yysa_W8n99vc_zcHxetNl4qo8gNx1qZu63I0H5UTYAI"; const invalidToken9 = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c.abc"; + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c.abc"; test("input is a valid JWT", () => { const result1 = validateJwtFormat(tokenHS256); @@ -32,40 +34,61 @@ describe("validateJwtFormat", () => { expect(result1.isOk()).toBe(true); result1.map((value) => expect(value).toStrictEqual({ - signingAlgorithm: "HS256", - type: JwtTypeValues.MACed, - encoded: { - token: - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", - header: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", - payload: - "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ", - signature: "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", - }, decoded: { header: { alg: "HS256", typ: "JWT", }, + payload: { + admin: true, + sub: "1234567890", + name: "John Doe", + iat: 1516239022, + }, + }, + signingAlgorithm: "HS256", + type: JwtTypeValues.MACed, + }) + ); + + const result2 = validateJwtFormat(tokenHS384); + expect(result2.isErr()).toBe(false); + expect(result2.isOk()).toBe(true); + result2.map((value) => + expect(value).toStrictEqual({ + type: JwtTypeValues.MACed, + signingAlgorithm: "HS384", + decoded: { + header: { alg: "HS384", typ: "JWT" }, + payload: { + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, + }, + }, + }) + ); + + const result3 = validateJwtFormat(tokenHS512); + expect(result3.isErr()).toBe(false); + expect(result3.isOk()).toBe(true); + result3.map((value) => + expect(value).toStrictEqual({ + type: JwtTypeValues.MACed, + signingAlgorithm: "HS512", + decoded: { + header: { alg: "HS512", typ: "JWT" }, payload: { sub: "1234567890", name: "John Doe", + admin: true, iat: 1516239022, }, }, - }), - ); - - // const result2 = validateJwtFormat(tokenHS384); - // expect(result2.isErr()).toBe(false); - // expect(result2.isOk()).toBe(true); - // result2.map((value) => expect(value).toBe(tokenHS384)); - // - // const result3 = validateJwtFormat(tokenHS512); - // expect(result3.isErr()).toBe(false); - // expect(result3.isOk()).toBe(true); - // result3.map((value) => expect(value).toBe(tokenHS512)); - // + }) + ); + const result4 = validateJwtFormat(tokenRS256); expect(result4.isErr()).toBe(false); expect(result4.isOk()).toBe(true); @@ -73,15 +96,6 @@ describe("validateJwtFormat", () => { expect(value).toStrictEqual({ signingAlgorithm: "RS256", type: JwtTypeValues.DigitallySigned, - encoded: { - token: - "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", - header: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9", - payload: - "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0", - signature: - "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", - }, decoded: { header: { alg: "RS256", @@ -94,19 +108,47 @@ describe("validateJwtFormat", () => { iat: 1516239022, }, }, - }), - ); - - // const result5 = validateJwtFormat(tokenRS384); - // expect(result5.isErr()).toBe(false); - // expect(result5.isOk()).toBe(true); - // result5.map((value) => expect(value).toBe(tokenRS384)); - // - // const result6 = validateJwtFormat(tokenRS512); - // expect(result6.isErr()).toBe(false); - // expect(result6.isOk()).toBe(true); - // result6.map((value) => expect(value).toBe(tokenRS512)); - // + }) + ); + + const result5 = validateJwtFormat(tokenRS384); + expect(result5.isErr()).toBe(false); + expect(result5.isOk()).toBe(true); + result5.map((value) => + expect(value).toStrictEqual({ + type: JwtTypeValues.DigitallySigned, + signingAlgorithm: "RS384", + decoded: { + header: { alg: "RS384", typ: "JWT" }, + payload: { + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, + }, + }, + }) + ); + + const result6 = validateJwtFormat(tokenRS512); + expect(result6.isErr()).toBe(false); + expect(result6.isOk()).toBe(true); + result6.map((value) => + expect(value).toStrictEqual({ + type: JwtTypeValues.DigitallySigned, + signingAlgorithm: "RS512", + decoded: { + header: { alg: "RS512", typ: "JWT" }, + payload: { + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, + }, + }, + }) + ); + const result7 = validateJwtFormat(unsecured); expect(result7.isErr()).toBe(false); expect(result7.isOk()).toBe(true); @@ -114,14 +156,6 @@ describe("validateJwtFormat", () => { expect(value).toStrictEqual({ signingAlgorithm: "none", type: JwtTypeValues.Unsecured, - encoded: { - token: - "eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.", - header: "eyJhbGciOiJub25lIn0", - payload: - "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", - signature: "", - }, decoded: { header: { alg: "none", @@ -132,7 +166,7 @@ describe("validateJwtFormat", () => { "http://example.com/is_root": true, }, }, - }), + }) ); }); @@ -141,81 +175,105 @@ describe("validateJwtFormat", () => { expect(result1.isErr()).toBe(true); expect(result1.isOk()).toBe(false); result1.mapErr((error) => - expect(error).toStrictEqual([ - "The first segment, the JWT header, and the second segment, the JWT payload, must represent a completely valid JSON object conforming to RFC 7159.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result2 = validateJwtFormat(invalidToken2); expect(result2.isErr()).toBe(true); expect(result2.isOk()).toBe(false); result2.mapErr((error) => - expect(error).toStrictEqual([ - "The first segment, the JWT header, and the second segment, the JWT payload, must represent a completely valid JSON object conforming to RFC 7159.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result3 = validateJwtFormat(invalidToken3); expect(result3.isErr()).toBe(true); expect(result3.isOk()).toBe(false); result3.mapErr((error) => - expect(error).toStrictEqual([ - "The second (payload) segment cannot be an empty string.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result4 = validateJwtFormat(invalidToken4); expect(result4.isErr()).toBe(true); expect(result4.isOk()).toBe(false); result4.mapErr((error) => - expect(error).toStrictEqual([ - "The JWT must contain at least one period ('.') character. Source: https://datatracker.ietf.org/doc/html/rfc7519#section-7.2", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result5 = validateJwtFormat(invalidToken5); expect(result5.isErr()).toBe(true); expect(result5.isOk()).toBe(false); result5.mapErr((error) => - expect(error).toStrictEqual([ - "The JWT must contain at least one period ('.') character. Source: https://datatracker.ietf.org/doc/html/rfc7519#section-7.2", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `JWT must not be empty.`, + }) ); const result6 = validateJwtFormat(invalidToken6); expect(result6.isErr()).toBe(true); expect(result6.isOk()).toBe(false); result6.mapErr((error) => - expect(error).toStrictEqual([ - "Each JWT segment must be a base64url-encoded. The third (signature) segment isn't.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result7 = validateJwtFormat(invalidToken7); expect(result7.isErr()).toBe(true); expect(result7.isOk()).toBe(false); result7.mapErr((error) => - expect(error).toStrictEqual([ - "The second segment, the JWT payload, must represent a completely valid JSON object conforming to RFC 7159.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `The second segment, the JWT payload, must represent a completely valid JSON object conforming to [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519#section-3).`, + data: { + header: { + alg: "HS256", + }, + payload: "test", + }, + }) ); const result8 = validateJwtFormat(invalidToken8); expect(result8.isErr()).toBe(true); expect(result8.isOk()).toBe(false); result8.mapErr((error) => - expect(error).toStrictEqual([ - "The first segment, the JWT header, and the second segment, the JWT payload, must represent a completely valid JSON object conforming to RFC 7159.", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); const result9 = validateJwtFormat(invalidToken9); expect(result9.isErr()).toBe(true); expect(result9.isOk()).toBe(false); result9.mapErr((error) => - expect(error).toStrictEqual([ - "This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters. Source: https://datatracker.ietf.org/doc/html/rfc7516#section-9", - ]), + expect(error).toStrictEqual({ + task: DebuggerTaskValues.DECODE, + input: DebuggerInputValues.JWT, + message: `This tool only supports a JWT that uses the JWS Compact Serialization, which must have three base64url-encoded segments separated by two period ('.') characters as defined on [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515#section-3.3)`, + }) ); }); }); From 54ae9619dbaf20143a398e58a719b40da9b0ca80 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:58:28 -0500 Subject: [PATCH 02/12] add action to run tests --- .github/workflows/tests.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/tests.yaml diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 00000000..8ff15f76 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,26 @@ +name: Run Tests + +on: + pull_request: + branches: [master] + push: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Install dependencies + run: npm ci + + - name: Run Vitest + run: npx vitest run + + \ No newline at end of file From 4fcca62e3657f224c8c4c6ab5a5cca430742b53f Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:58:38 -0500 Subject: [PATCH 03/12] fix type error --- src/features/common/services/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/common/services/utils.ts b/src/features/common/services/utils.ts index a6d69d8e..67833864 100644 --- a/src/features/common/services/utils.ts +++ b/src/features/common/services/utils.ts @@ -84,7 +84,7 @@ export const safeJsonStringify = fromThrowable(JSON.stringify, (e) => { }); export const safeNewUint8ArrayFromBuffer = fromThrowable( - (buffer: ArrayBufferLike) => new Uint8Array(buffer), + (buffer: Buffer) => new Uint8Array(buffer), (e) => { if (e instanceof Error) { return e.message; From 634f7ed80646c787cf15cf95b82671c18522995a Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:03:26 -0500 Subject: [PATCH 04/12] ignore e2e tests for vitest --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index b8aa5767..e278344a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ coverage: { provider: "istanbul", }, + exclude: ['e2e', 'node_modules'] }, plugins: [tsconfigPaths()], }); From 96c0f87b407483107a60b51c92423f9311db747e Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:08:12 -0500 Subject: [PATCH 05/12] run e2e tests in CI --- .github/workflows/production.yaml | 20 ++++++++++++++++++++ .github/workflows/tests.yaml | 3 +++ 2 files changed, 23 insertions(+) diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index 371423c2..0a1f01e4 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -8,7 +8,27 @@ on: - master - production jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Install dependencies + run: npm ci + + - name: Run Vitest + run: npx vitest run + + - name: Install Playwright Browsers + run: npx playwright test + Deploy-Production: + needs: test runs-on: labels: ubuntu-latest steps: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8ff15f76..e49e6c41 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -22,5 +22,8 @@ jobs: - name: Run Vitest run: npx vitest run + + - name: Install Playwright Browsers + run: npx playwright test \ No newline at end of file From a898c8335da2b99b62a20060866a6b70640d06a0 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:13:48 -0500 Subject: [PATCH 06/12] fix playwright commands --- .github/workflows/production.yaml | 3 +++ .github/workflows/tests.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index 0a1f01e4..7a56be1a 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -25,6 +25,9 @@ jobs: run: npx vitest run - name: Install Playwright Browsers + run: npx playwright install --with-deps + + - name: Run Playwright Tests run: npx playwright test Deploy-Production: diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index e49e6c41..2016b69e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -24,6 +24,9 @@ jobs: run: npx vitest run - name: Install Playwright Browsers + run: npx playwright install --with-deps + + - name: Run Playwright Tests run: npx playwright test \ No newline at end of file From f92bd1265e224ddfcc035c70d4e5158c622c86e1 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:31:45 -0500 Subject: [PATCH 07/12] fix firefox browser permissions --- e2e/decoder.spec.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/decoder.spec.ts b/e2e/decoder.spec.ts index 7ecfd1c9..27963f7d 100644 --- a/e2e/decoder.spec.ts +++ b/e2e/decoder.spec.ts @@ -53,10 +53,11 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { await expect(jwtEditorInput).toHaveValue(inputValue); }); - test("can copy value in JWT editor", async ({ page, context }) => { + test("can copy value in JWT editor", async ({ page, context, browserName }) => { + const permissions = browserName === 'firefox' ? [] : ["clipboard-read", "clipboard-write"] const inputValue = (TestJwts.RS512 as JwtSignedWithDigitalModel).withPemKey .jwt; - await context.grantPermissions(["clipboard-read", "clipboard-write"]); + await context.grantPermissions(permissions); const lang = await getLang(page); expectToBeNonNull(lang); From afc63872318e591c9c33adcbce7201d2f19e309d Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:32:29 -0500 Subject: [PATCH 08/12] fix formatting --- .github/workflows/production.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index 7a56be1a..9955a54c 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -25,7 +25,7 @@ jobs: run: npx vitest run - name: Install Playwright Browsers - run: npx playwright install --with-deps + run: npx playwright install --with-deps - name: Run Playwright Tests run: npx playwright test From 00ac90ac9e1861cb993af22000aadd07ae9fcf0c Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:32:44 -0500 Subject: [PATCH 09/12] remove webkit option --- playwright.config.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index efd9b88b..40a61dc3 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -44,11 +44,6 @@ export default defineConfig({ use: { ...devices['Desktop Firefox'] }, }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - /* Test against mobile viewports. */ // { // name: 'Mobile Chrome', From 249058d8dd9c29f9bcc6dc28c03b33a37f238254 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:18:23 -0500 Subject: [PATCH 10/12] add webserver config for playwright --- playwright.config.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index 40a61dc3..62cec27f 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -66,9 +66,10 @@ export default defineConfig({ ], /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, + webServer: { + command: 'npm run dev', + port: 1234, + timeout: 60 * 1000, + reuseExistingServer: !process.env.CI, + }, }); From 9623af2157a4957ad93c61d11065a65c6d23ab48 Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:52:19 -0500 Subject: [PATCH 11/12] add trace zip upload to test action --- .github/workflows/tests.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 2016b69e..7627c212 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -28,5 +28,12 @@ jobs: - name: Run Playwright Tests run: npx playwright test + + - name: Upload Playwright Traces + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-traces + path: playwright-report/**/trace.zip \ No newline at end of file From 3532fd8f2c54fe4b4d8b90f5e45b95ae449fa4cf Mon Sep 17 00:00:00 2001 From: Javier Tinoco <213990346+javiert-okta@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:05:05 -0500 Subject: [PATCH 12/12] add traces upload step to prod action --- .github/workflows/production.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index 9955a54c..ab6f8cf0 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -29,6 +29,13 @@ jobs: - name: Run Playwright Tests run: npx playwright test + + - name: Upload Playwright Traces + if: failure() + uses: actions/upload-artifact@v4 + with: + name: playwright-traces + path: playwright-report/**/trace.zip Deploy-Production: needs: test