From 9b7c8e8a6b915c2afeb46013d75a1c24a436e177 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Mon, 29 Aug 2022 15:00:37 +0200 Subject: [PATCH] feat: test helpers --- package-lock.json | 4 +- package.json | 3 +- packages/test/package.json | 4 +- packages/test/vite.config.ts | 1 + .../ory/self-service-auth-card.spec.tsx | 370 ++--------- src/test/AuthPage.ts | 45 ++ src/test/Fixtures.ts | 604 ++++++++++++++++++ src/test/Login.ts | 36 -- src/test/Registration.ts | 35 - src/test/Utils.ts | 23 + src/test/index.ts | 3 +- src/test/types.ts | 3 +- 12 files changed, 729 insertions(+), 402 deletions(-) create mode 100644 src/test/AuthPage.ts create mode 100644 src/test/Fixtures.ts delete mode 100644 src/test/Login.ts delete mode 100644 src/test/Registration.ts create mode 100644 src/test/Utils.ts diff --git a/package-lock.json b/package-lock.json index c70d6da49..ce54c1f45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49756,8 +49756,8 @@ "@ory/client": "^0.2.0-alpha.21", "@ory/elements-preact": "*", "@preact/preset-vite": "^2.3.0", - "eslint": "*", - "eslint-config-preact": "*", + "eslint": "^8.22.0", + "eslint-config-preact": "^1.3.0", "preact": "^10.10.1", "typescript": "^4.6.4", "vite": "^3.0.7", diff --git a/package.json b/package.json index 248775c49..0fcd8e2da 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ "typescript": "^4.7.4", "vite": "^3.0.9", "vite-plugin-dts": "^1.4.1", - "wait-on": "^6.0.1" + "wait-on": "^6.0.1", + "@ory/elements-test": "*" }, "files": [ "dist/*" diff --git a/packages/test/package.json b/packages/test/package.json index 17bab75c3..3155b9018 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -9,8 +9,8 @@ "import": "./dist/index.mjs", "require": "./dist/index.umd.js" }, - "./style.css": "./dist/style.css", - "./package.json": "./package.json" + "./package.json": "./package.json", + "./assets/*.json": "./dist/assets/*.json" }, "main": "./dist/index.umd.js", "module": "./dist/index.mjs", diff --git a/packages/test/vite.config.ts b/packages/test/vite.config.ts index 76ea16484..eef6cc91d 100644 --- a/packages/test/vite.config.ts +++ b/packages/test/vite.config.ts @@ -19,6 +19,7 @@ export default defineConfig({ }, rollupOptions: { treeshake: "smallest", + external: ["@playwright/test"], }, }, }) diff --git a/src/react-components/ory/self-service-auth-card.spec.tsx b/src/react-components/ory/self-service-auth-card.spec.tsx index 3662000df..6c819321d 100644 --- a/src/react-components/ory/self-service-auth-card.spec.tsx +++ b/src/react-components/ory/self-service-auth-card.spec.tsx @@ -1,173 +1,18 @@ import React from "react" import { expect, test } from "@playwright/experimental-ct-react" import { SelfServiceAuthCard } from "./self-service-auth-card" -import { SelfServiceFlow } from "../../types" import { Locator } from "playwright-core" - -const defaultFlow: SelfServiceFlow = { - id: "", - state: "choose_method", - type: "browser", - ui: { - action: "https://test.com", - method: "POST", - nodes: [ - { - group: "default", - attributes: { - name: "id", - type: "text", - node_type: "input", - disabled: false, - required: true, - }, - messages: [], - type: "input", - meta: { - label: { - id: 1, - text: "ID", - type: "text", - }, - }, - }, - { - group: "default", - attributes: { - name: "csrf_token", - type: "hidden", - node_type: "input", - disabled: false, - }, - messages: [], - type: "input", - meta: {}, - }, - { - group: "password", - attributes: { - name: "password", - type: "password", - node_type: "input", - disabled: false, - required: true, - }, - messages: [], - type: "input", - meta: { - label: { - id: 1, - text: "Password", - type: "text", - }, - }, - }, - { - group: "password", - attributes: { - name: "submit", - type: "submit", - node_type: "input", - disabled: false, - }, - messages: [], - type: "input", - meta: { - label: { - id: 1, - text: "Submit", - type: "text", - }, - }, - }, - ], - }, -} - -const alternativeFlow: SelfServiceFlow = { - id: "", - state: "choose_method", - type: "browser", - ui: { - action: "https://test.com", - method: "POST", - nodes: [ - { - type: "input", - group: "default", - attributes: { - name: "csrf_token", - type: "hidden", - value: "", - required: true, - disabled: false, - node_type: "input", - }, - messages: [], - meta: {}, - }, - { - type: "input", - group: "link", - attributes: { - name: "email", - type: "email", - required: true, - disabled: false, - node_type: "input", - }, - messages: [], - meta: { - label: { - id: 1070007, - text: "Email", - type: "info", - }, - }, - }, - { - type: "input", - group: "link", - attributes: { - name: "method", - type: "submit", - value: "link", - disabled: false, - node_type: "input", - }, - messages: [], - meta: { - label: { - id: 1070005, - text: "Submit", - type: "info", - }, - }, - }, - ], - }, -} - -const expectDefaultFlow = async (component: Locator) => { - await expect(component).toContainText("ID *") - await expect(component).toContainText("Password *") - await expect(component.locator('input[name="id"]')).toBeVisible() - await expect(component.locator('input[name="password"]')).toBeVisible() - await expect(component.locator('input[name="csrf_token"]')).toBeHidden() - await expect(component.locator('button[name="submit"]')).toBeVisible() - await expect(component.locator('button[name="submit"]')).toHaveText("Submit") - await expect( - component.locator('form[action="https://test.com"]'), - ).toBeVisible() -} - -const expectAlternativeFlow = async (component: Locator) => { - await expect(component).toContainText("Email *") - await expect(component.locator('button[type="submit"]')).toBeVisible() - await expect(component.locator('button[type="submit"]')).toContainText( - "Submit", - ) -} +import { + loginFixture, + registrationFixture, + registrationPasswordlessFixture, + loginPasswordlessFixture, + registrationNoneFixture, + loginNoneFixture, + verificationFixture, + recoveryFixture, + AuthPage, +} from "@ory/elements-test" test("ory auth card login flow", async ({ mount }) => { const component = await mount( @@ -178,9 +23,14 @@ test("ory auth card login flow", async ({ mount }) => { forgotPasswordURL: "/forgot", signupURL: "/signup", }} - flow={defaultFlow} + flow={loginFixture} />, ) + + const loginComponent = new AuthPage(loginFixture.ui.nodes, component) + await loginComponent.expectTraitFields() + await loginComponent.expectTraitLabels() + await expect(component).toContainText("Sign in") await expect(component).toContainText("Forgot password?", { ignoreCase: true, @@ -194,8 +44,6 @@ test("ory auth card login flow", async ({ mount }) => { await expect(component).toContainText("Don't have an account", { ignoreCase: true, }) - - await expectDefaultFlow(component) }) test("ory auth card registration flow", async ({ mount }) => { @@ -206,10 +54,18 @@ test("ory auth card registration flow", async ({ mount }) => { additionalProps={{ loginURL: "/login", }} - flow={defaultFlow} + flow={registrationFixture} />, ) + const registrationComponent = new AuthPage( + registrationFixture.ui.nodes, + component, + ) + + await registrationComponent.expectTraitFields() + await registrationComponent.expectTraitFields() + await expect(component).toContainText("Sign up", { ignoreCase: true }) await expect(component).toContainText("Already have an account?", { ignoreCase: true, @@ -218,13 +74,15 @@ test("ory auth card registration flow", async ({ mount }) => { component.locator('a[data-testid="login-link"]'), ).toHaveAttribute("href", "/login") - await expectDefaultFlow(component) + const submit = await component.locator('button[type="submit"]') + await expect(submit).toBeVisible() + await expect(submit).toHaveText("Sign up") }) test("ory auth card verification flow", async ({ mount }) => { const component = await mount( { />, ) + const verificationComponent = new AuthPage( + verificationFixture.ui.nodes, + component, + ) + await verificationComponent.expectTraitFields() + await verificationComponent.expectTraitLabels() + await expect(component).toContainText("Verification") await expect(component.locator('a[href="/signup"]')).toBeVisible() - await expectAlternativeFlow(component) }) test("ory auth card recovery flow", async ({ mount }) => { const component = await mount( { title={"Recovery"} />, ) + + const recoveryComponent = new AuthPage(recoveryFixture.ui.nodes, component) + await recoveryComponent.expectTraitFields() + await recoveryComponent.expectTraitLabels() + await expect(component).toContainText("Recovery") await expect(component.locator('a[href="/login"]')).toBeVisible() - - await expectAlternativeFlow(component) }) +// TODO: change to 2fa flow fixture test("ory auth card login 2fa flow", async ({ mount }) => { const component = await mount( { ) expect(component).toContainText("Two-factor authentication") - expect(component.locator("input[name='totp_code']")).toBeVisible() - expect(component.locator('button[type="submit"]')).toBeVisible() expect(component.locator('a[href="/logout"]')).toBeVisible() + expect( component.locator('button[name="webauthn_login_trigger"]'), ).toBeVisible() diff --git a/src/test/AuthPage.ts b/src/test/AuthPage.ts new file mode 100644 index 000000000..9999b4cce --- /dev/null +++ b/src/test/AuthPage.ts @@ -0,0 +1,45 @@ +import { UiNode } from "@ory/client" +import { expect, Locator } from "@playwright/test" +import { Traits } from "./types" +import { inputNodesToRecord, isUiNode } from "./Utils" + +export class AuthPage { + readonly locator: Locator + readonly traits: Record + readonly formFields: Record = {} + + constructor(traits: Record | UiNode[], locator: Locator) { + this.locator = locator + this.traits = isUiNode(traits) ? inputNodesToRecord(traits) : traits + for (const key in traits) { + this.formFields[key] = locator.locator(`input[name="${key}"]`) + } + } + + async expectTraitFields() { + for (const key in this.traits) { + if (this.traits[key].type === "hidden") { + await expect(this.locator.locator(`*[name="${key}"]`)).toBeHidden() + } else { + await expect(this.locator.locator(`*[name="${key}"]`)).toBeVisible() + } + } + } + + async expectTraitLabels() { + for (const key in this.traits) { + await expect(this.locator).toContainText(this.traits[key].label) + } + } + + async submitForm() { + for (const key in this.traits) { + if (this.traits[key].type === "input") { + await this.formFields[key].fill(this.traits[key].value) + } else if (this.traits[key].type === "checkbox") { + await this.formFields[key].click() + } + } + await this.locator.locator('[type="submit"]').click() + } +} diff --git a/src/test/Fixtures.ts b/src/test/Fixtures.ts new file mode 100644 index 000000000..4a9444ba6 --- /dev/null +++ b/src/test/Fixtures.ts @@ -0,0 +1,604 @@ +import { + SelfServiceLoginFlow, + SelfServiceRecoveryFlow, + SelfServiceRegistrationFlow, + SelfServiceVerificationFlow, +} from "@ory/client" + +export const loginFixture: SelfServiceLoginFlow = { + id: "31f9170d-c28d-4f6c-8cf8-54b999a4f172", + type: "browser", + expires_at: "2022-08-29T10:21:13.298704656Z", + issued_at: "2022-08-29T09:51:13.298704656Z", + request_url: "http://test.com/self-service/login/browser", + ui: { + action: + "http://test.com/self-service/login?flow=31f9170d-c28d-4f6c-8cf8-54b999a4f172", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: + "gRBwj0LpxhDugD8sNq1/H6HABvVKMoZepLuLHU+7leIFL33RU4hc1sCYlFKRlsqYPBAG5P84ADrnwLLC3TIy3g==", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "default", + attributes: { + name: "identifier", + type: "text", + value: "", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070004, + text: "ID", + type: "info", + }, + }, + }, + { + type: "input", + group: "password", + attributes: { + name: "password", + type: "password", + required: true, + autocomplete: "current-password", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070001, + text: "Password", + type: "info", + }, + }, + }, + { + type: "input", + group: "password", + attributes: { + name: "method", + type: "submit", + value: "password", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1010001, + text: "Sign in", + type: "info", + context: {}, + }, + }, + }, + ], + }, + created_at: "2022-08-29T09:51:13.308547Z", + updated_at: "2022-08-29T09:51:13.308547Z", + refresh: false, + requested_aal: "aal1", +} + +export const loginPasswordlessFixture: SelfServiceLoginFlow = { + id: "13de599b-5fc0-472c-9635-b1d19c0ea9e3", + type: "browser", + expires_at: "2022-08-29T10:22:39.093619222Z", + issued_at: "2022-08-29T09:52:39.093619222Z", + request_url: "http://test.com/self-service/login/browser", + ui: { + action: + "http://test.com/self-service/login?flow=13de599b-5fc0-472c-9635-b1d19c0ea9e3", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: + "ze0GFCqJ9j8C+ePPezAZl1j/Hu+7BKPXX8wxAe/Ky1xJ0gtKO+hs+SzhSLHcC6wQxS8e/g4OJbMctwjefUNsYA==", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "default", + attributes: { + name: "identifier", + type: "text", + value: "", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070004, + text: "ID", + type: "info", + }, + }, + }, + { + type: "input", + group: "webauthn", + attributes: { + name: "method", + type: "submit", + value: "webauthn", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1010001, + text: "Sign in with security key", + type: "info", + context: {}, + }, + }, + }, + ], + }, + created_at: "2022-08-29T09:52:39.101785Z", + updated_at: "2022-08-29T09:52:39.101785Z", + refresh: false, + requested_aal: "aal1", +} + +export const verificationFixture: SelfServiceVerificationFlow = { + id: "5c857a5a-6a21-48cb-9acd-da9b81c1ed13", + state: "choose_method", + type: "browser", + request_url: "https://test.com", + ui: { + action: "https://test.com", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: "", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "link", + attributes: { + name: "email", + type: "email", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070007, + text: "Email", + type: "info", + }, + }, + }, + { + type: "input", + group: "link", + attributes: { + name: "method", + type: "submit", + value: "link", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070005, + text: "Submit", + type: "info", + }, + }, + }, + ], + }, + issued_at: "2022-08-22T22:02:15.825471Z", + expires_at: "2022-08-22T22:02:15.825471Z", +} + +export const recoveryFixture: SelfServiceRecoveryFlow = { + id: "5c857a5a-6a21-48cb-9acd-da9b81c1ed13", + state: "choose_method", + type: "browser", + request_url: "https://test.com", + ui: { + action: "https://test.com", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: "", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "link", + attributes: { + name: "email", + type: "email", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070007, + text: "Email", + type: "info", + }, + }, + }, + { + type: "input", + group: "link", + attributes: { + name: "method", + type: "submit", + value: "link", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070005, + text: "Submit", + type: "info", + }, + }, + }, + ], + }, + issued_at: "2022-08-22T22:02:15.825471Z", + expires_at: "2022-08-22T22:02:15.825471Z", +} + +export const registrationPasswordlessFixture: SelfServiceRegistrationFlow = { + id: "e8cc19c1-66f1-4c22-8099-66126a420353", + type: "browser", + expires_at: "2022-08-29T10:12:12.731545764Z", + issued_at: "2022-08-29T09:42:12.731545764Z", + request_url: "http://test.com/self-service/registration/browser", + ui: { + action: + "http://test.com/self-service/registration?flow=e8cc19c1-66f1-4c22-8099-66126a420353", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "traits.email", + type: "email", + required: true, + autocomplete: "email", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070002, + text: "E-Mail", + type: "info", + }, + }, + }, + { + type: "input", + group: "default", + attributes: { + name: "traits.firstName", + type: "text", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070002, + text: "First Name", + type: "info", + }, + }, + }, + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: + "h37vdPr40Oc/jJJuh7Hi97MFYkITgp5eU51UP34qG3kDQeIq65lKIRGUORAgildwLtViU6aIGDoQ5m3g7KO8RQ==", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "webauthn", + attributes: { + name: "webauthn_register_displayname", + type: "text", + value: "", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1050013, + text: "Name of the security key", + type: "info", + }, + }, + }, + { + type: "input", + group: "webauthn", + attributes: { + name: "webauthn_register", + type: "hidden", + value: "", + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "webauthn", + attributes: { + name: "webauthn_register_trigger", + type: "button", + value: "", + disabled: false, + onclick: + 'window.__oryWebAuthnRegistration({"publicKey":{"challenge":"","rp":{"name":"Test","id":"test.com"},"user":{"name":"placeholder","icon":"https://via.placeholder.com/128","displayName":"placeholder","id":"xY1S9EHMQyGB3+MTZRt5jA=="},"pubKeyCredParams":[{"type":"public-key","alg":-7},{"type":"public-key","alg":-35},{"type":"public-key","alg":-36},{"type":"public-key","alg":-257},{"type":"public-key","alg":-258},{"type":"public-key","alg":-259},{"type":"public-key","alg":-37},{"type":"public-key","alg":-38},{"type":"public-key","alg":-39},{"type":"public-key","alg":-8}],"authenticatorSelection":{"userVerification":"discouraged"},"timeout":60000}})', + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1040004, + text: "Sign up with security key", + type: "info", + }, + }, + }, + { + type: "script", + group: "webauthn", + attributes: { + src: "http://test.com/.well-known/ory/webauthn.js", + async: true, + referrerpolicy: "no-referrer", + crossorigin: "anonymous", + integrity: "", + type: "text/javascript", + id: "webauthn_script", + nonce: "378c397a-1803-4d04-9dfb-33e575bd6942", + node_type: "script", + }, + messages: [], + meta: {}, + }, + ], + }, +} + +export const registrationFixture: SelfServiceRegistrationFlow = { + id: "1c09e31f-fb4c-4eac-8f17-ec7ae01cab97", + type: "browser", + expires_at: "2022-08-29T10:19:47.350346654Z", + issued_at: "2022-08-29T09:49:47.350346654Z", + request_url: "http://test.com/self-service/registration/browser", + ui: { + action: + "http://test.com/self-service/registration?flow=1c09e31f-fb4c-4eac-8f17-ec7ae01cab97", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: + "uWnnuBgYQAXyeCuZ+c6aBNfj55zPo+0ACTcp+rzrIcQ9VurmCXnaw9xggOde9S+DSjPnjXqpa2RKTBAlLmKG+A==", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + { + type: "input", + group: "password", + attributes: { + name: "traits.email", + type: "email", + required: true, + autocomplete: "email", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070002, + text: "E-Mail", + type: "info", + }, + }, + }, + { + type: "input", + group: "password", + attributes: { + name: "password", + type: "password", + required: true, + autocomplete: "new-password", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070001, + text: "Password", + type: "info", + }, + }, + }, + { + type: "input", + group: "password", + attributes: { + name: "traits.firstName", + type: "text", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1070002, + text: "First Name", + type: "info", + }, + }, + }, + { + type: "input", + group: "password", + attributes: { + name: "method", + type: "submit", + value: "password", + disabled: false, + node_type: "input", + }, + messages: [], + meta: { + label: { + id: 1040001, + text: "Sign up", + type: "info", + context: {}, + }, + }, + }, + ], + }, +} + +export const registrationNoneFixture: SelfServiceRegistrationFlow = { + id: "d045d585-0813-45fe-bb91-ef8d44e39dc9", + type: "browser", + expires_at: "2022-08-29T10:16:42.581562361Z", + issued_at: "2022-08-29T09:46:42.581562361Z", + request_url: "http://test.com/self-service/registration/browser", + ui: { + action: + "http://test.com/self-service/registration?flow=d045d585-0813-45fe-bb91-ef8d44e39dc9", + method: "POST", + nodes: [], + }, +} + +export const loginNoneFixture: SelfServiceLoginFlow = { + id: "62a8739b-5866-4a4c-9951-acb39a49709c", + type: "browser", + expires_at: "2022-08-29T10:17:27.87015684Z", + issued_at: "2022-08-29T09:47:27.87015684Z", + request_url: "http://test.com/self-service/login/browser", + ui: { + action: + "http://test.com/self-service/login?flow=62a8739b-5866-4a4c-9951-acb39a49709c", + method: "POST", + nodes: [ + { + type: "input", + group: "default", + attributes: { + name: "csrf_token", + type: "hidden", + value: + "FFydsmckUnGEN4NMGn5uuW1TTy0XNTd1YrAIS6szdbuQY5DsdkXIt6ovKDK9Rds+8INPPKI/sREhyzGUObrShw==", + required: true, + disabled: false, + node_type: "input", + }, + messages: [], + meta: {}, + }, + ], + }, + created_at: "2022-08-29T09:47:27.876079Z", + updated_at: "2022-08-29T09:47:27.876079Z", + refresh: false, + requested_aal: "aal1", +} diff --git a/src/test/Login.ts b/src/test/Login.ts deleted file mode 100644 index 2c7ae13c6..000000000 --- a/src/test/Login.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Locator, Page } from "@playwright/test" -import { Traits } from "./types" - -export default class Login { - public page: Page - readonly traits: Record - readonly id: Locator - readonly password: Locator - readonly submitButton: Locator - readonly formFields: Record = {} - - constructor(traits: Record, page: Page) { - this.page = page - this.traits = traits - this.id = page.locator(`input[name="identifier"]`) - this.password = page.locator(`input[name="password"]`) - this.submitButton = page.locator(`button[type="submit"]`) - for (const key in traits) { - this.formFields[key] = page.locator(`input[name="${key}"]`) - } - } - - async login(traits?: Record) { - if (!traits) { - traits = this.traits - } - - for (const key in traits) { - if (traits[key].type === "input") { - await this.formFields[key].fill(traits[key].value) - } else if (traits[key].type === "checkbox") { - await this.formFields[key].click() - } - } - } -} diff --git a/src/test/Registration.ts b/src/test/Registration.ts deleted file mode 100644 index e57201fcd..000000000 --- a/src/test/Registration.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Locator, Page } from "@playwright/test" -import { Traits } from "./types" - -export default class Registration { - public page: Page - readonly traits: Record - readonly id: Locator - readonly password: Locator - readonly submitButton: Locator - readonly formFields: Record = {} - - constructor(traits: Record, page: Page) { - this.page = page - this.traits = traits - this.id = page.locator(`input[name="identifier"]`) - this.password = page.locator(`input[name="password"]`) - this.submitButton = page.locator(`button[type="submit"]`) - for (const key in traits) { - this.formFields[key] = page.locator(`input[name="${key}"]`) - } - } - - async register(traits?: Record) { - if (!traits) { - traits = this.traits - } - for (const key in traits) { - if (traits[key].type === "input") { - await this.formFields[key].fill(traits[key].value) - } else if (traits[key].type === "checkbox") { - await this.formFields[key].click() - } - } - } -} diff --git a/src/test/Utils.ts b/src/test/Utils.ts new file mode 100644 index 000000000..db937362f --- /dev/null +++ b/src/test/Utils.ts @@ -0,0 +1,23 @@ +import { UiNode } from "@ory/client" +import { isUiNodeInputAttributes } from "@ory/integrations/ui" +import { Traits } from "./types" + +export const isUiNode = (a: unknown): a is UiNode[] => { + return ( + Array.isArray(a) && + a.some((v) => typeof v === "object" && v !== null && "attributes" in v) + ) +} + +export const inputNodesToRecord = (nodes: UiNode[]): Record => { + return nodes.reduce((map: Record, { attributes }) => { + if (isUiNodeInputAttributes(attributes)) { + map[attributes.name] = { + value: attributes.value, + type: attributes.type as Traits["type"], + label: attributes.label?.text || "", + } + } + return map + }, {}) +} diff --git a/src/test/index.ts b/src/test/index.ts index 2cf84eb93..33435e04c 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -1 +1,2 @@ -export * from "./Login" +export * from "./AuthPage" +export * from "./Fixtures" diff --git a/src/test/types.ts b/src/test/types.ts index 7632faccb..427cc34b5 100644 --- a/src/test/types.ts +++ b/src/test/types.ts @@ -1,4 +1,5 @@ export type Traits = { value: string - type: "input" | "checkbox" | "button" + type: "input" | "checkbox" | "button" | "hidden" | "submit" + label: string }