snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
This commit is contained in:
269
test/e2e/playwright/models/elements/login.ts
Normal file
269
test/e2e/playwright/models/elements/login.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright © 2024 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { expect, Locator, Page } from "@playwright/test"
|
||||
import { createInputLocator, InputLocator } from "../../selectors/input"
|
||||
import { URLSearchParams } from "node:url"
|
||||
import { OryKratosConfiguration } from "../../../shared/config"
|
||||
|
||||
enum LoginStyle {
|
||||
IdentifierFirst = "identifier_first",
|
||||
Unified = "unified",
|
||||
}
|
||||
|
||||
type SubmitOptions = {
|
||||
submitWithKeyboard?: boolean
|
||||
waitForURL?: string | RegExp
|
||||
}
|
||||
|
||||
export class LoginPage {
|
||||
public submitPassword: Locator
|
||||
public github: Locator
|
||||
public google: Locator
|
||||
public signup: Locator
|
||||
|
||||
public identifier: InputLocator
|
||||
public password: InputLocator
|
||||
public totpInput: InputLocator
|
||||
public totpSubmit: Locator
|
||||
public lookupInput: InputLocator
|
||||
public lookupSubmit: Locator
|
||||
public codeSubmit = this.page.locator(
|
||||
'button[type="submit"][name="method"][value="code"]',
|
||||
)
|
||||
public codeSubmitMfa = this.page.locator(
|
||||
'button[type="submit"][name="address"]',
|
||||
)
|
||||
public codeInput = createInputLocator(this.page, "code")
|
||||
|
||||
public alert: Locator
|
||||
|
||||
constructor(
|
||||
readonly page: Page,
|
||||
readonly config: OryKratosConfiguration,
|
||||
) {
|
||||
this.identifier = createInputLocator(page, "identifier")
|
||||
this.password = createInputLocator(page, "password")
|
||||
this.totpInput = createInputLocator(page, "totp_code")
|
||||
this.lookupInput = createInputLocator(page, "lookup_secret")
|
||||
|
||||
this.submitPassword = page.locator(
|
||||
'[type="submit"][name="method"][value="password"]',
|
||||
)
|
||||
|
||||
this.github = page.locator('[name="provider"][value="github"]')
|
||||
this.google = page.locator('[name="provider"][value="google"]')
|
||||
|
||||
this.totpSubmit = page.locator('[name="method"][value="totp"]')
|
||||
this.lookupSubmit = page.locator('[name="method"][value="lookup_secret"]')
|
||||
|
||||
this.signup = page.locator('[data-testid="signup-link"]')
|
||||
|
||||
// this.submitHydra = page.locator('[name="provider"][value="hydra"]')
|
||||
// this.forgotPasswordLink = page.locator(
|
||||
// "[data-testid='forgot-password-link']",
|
||||
// )
|
||||
// this.logoutLink = page.locator("[data-testid='logout-link']")
|
||||
}
|
||||
|
||||
async submitIdentifierFirst(identifier: string) {
|
||||
await this.inputField("identifier").fill(identifier)
|
||||
await this.submit("identifier_first", {
|
||||
waitForURL: new RegExp(this.config.selfservice.flows.login.ui_url),
|
||||
})
|
||||
}
|
||||
|
||||
async loginWithPassword(
|
||||
identifier: string,
|
||||
password: string,
|
||||
opts?: SubmitOptions,
|
||||
) {
|
||||
switch (this.config.selfservice.flows.login.style) {
|
||||
case LoginStyle.IdentifierFirst:
|
||||
await this.submitIdentifierFirst(identifier)
|
||||
break
|
||||
case LoginStyle.Unified:
|
||||
await this.inputField("identifier").fill(identifier)
|
||||
break
|
||||
}
|
||||
|
||||
await this.inputField("password").fill(password)
|
||||
await this.submit("password", opts)
|
||||
}
|
||||
|
||||
async triggerLoginWithCode(identifier: string, opts?: SubmitOptions) {
|
||||
switch (this.config.selfservice.flows.login.style) {
|
||||
case LoginStyle.IdentifierFirst:
|
||||
await this.submitIdentifierFirst(identifier)
|
||||
const enabled = [
|
||||
this.config.selfservice.methods.passkey?.enabled,
|
||||
this.config.selfservice.methods.password?.enabled,
|
||||
this.config.selfservice.methods.code?.passwordless_enabled,
|
||||
this.config.selfservice.methods.webauthn?.enabled &&
|
||||
this.config.selfservice.methods.webauthn?.config?.passwordless,
|
||||
this.config.selfservice.methods.oidc?.enabled &&
|
||||
this.config.selfservice.methods.oidc.config.providers.length > 0,
|
||||
].filter((e) => e)
|
||||
|
||||
if (enabled.length > 1) {
|
||||
await this.codeSubmit.click()
|
||||
}
|
||||
break
|
||||
case LoginStyle.Unified:
|
||||
await this.inputField("identifier").fill(identifier)
|
||||
await this.codeSubmit.click()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
async open({
|
||||
aal,
|
||||
refresh,
|
||||
}: {
|
||||
aal?: string
|
||||
refresh?: boolean
|
||||
} = {}) {
|
||||
const p = new URLSearchParams()
|
||||
if (refresh) {
|
||||
p.append("refresh", "true")
|
||||
}
|
||||
|
||||
if (aal) {
|
||||
p.append("aal", aal)
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this.page.goto(
|
||||
this.config.selfservice.flows.login.ui_url + "?" + p.toString(),
|
||||
),
|
||||
this.isReady(),
|
||||
this.page.waitForURL((url) =>
|
||||
url.toString().includes(this.config.selfservice.flows.login.ui_url),
|
||||
),
|
||||
])
|
||||
await this.isReady()
|
||||
}
|
||||
|
||||
async isReady() {
|
||||
await expect(this.inputField("csrf_token").nth(0)).toBeHidden()
|
||||
}
|
||||
|
||||
submitMethod(method: string) {
|
||||
switch (method) {
|
||||
case "google":
|
||||
case "github":
|
||||
case "hydra":
|
||||
return this.page.locator(`[name="provider"][value="${method}"]`)
|
||||
}
|
||||
return this.page.locator(`[name="method"][value="${method}"]`)
|
||||
}
|
||||
|
||||
inputField(name: string) {
|
||||
return this.page.locator(`input[name=${name}]`)
|
||||
}
|
||||
|
||||
async submit(method: string, opts?: SubmitOptions) {
|
||||
const waitFor = [
|
||||
opts?.waitForURL
|
||||
? this.page.waitForURL(opts.waitForURL)
|
||||
: Promise.resolve(),
|
||||
]
|
||||
|
||||
if (opts?.submitWithKeyboard) {
|
||||
waitFor.push(this.page.keyboard.press("Enter"))
|
||||
} else {
|
||||
waitFor.push(this.submitMethod(method).click())
|
||||
}
|
||||
|
||||
await Promise.all(waitFor)
|
||||
}
|
||||
|
||||
//
|
||||
// async submitPasswordForm(
|
||||
// id: string,
|
||||
// password: string,
|
||||
// expectURL: string | RegExp,
|
||||
// options: {
|
||||
// submitWithKeyboard?: boolean
|
||||
// style?: LoginStyle
|
||||
// } = {
|
||||
// submitWithKeyboard: false,
|
||||
// style: LoginStyle.OneStep,
|
||||
// },
|
||||
// ) {
|
||||
// await this.isReady()
|
||||
// await this.inputField("identifier").fill(id)
|
||||
//
|
||||
// if (options.style === LoginStyle.IdentifierFirst) {
|
||||
// await this.submitMethod("identifier_first").click()
|
||||
// await this.inputField("password").fill(password)
|
||||
// } else {
|
||||
// await this.inputField("password").fill(password)
|
||||
// }
|
||||
//
|
||||
// const nav = this.page.waitForURL(expectURL)
|
||||
//
|
||||
// if (submitWithKeyboard) {
|
||||
// await this.page.keyboard.press("Enter")
|
||||
// } else {
|
||||
// await this.submitPassword.click()
|
||||
// }
|
||||
//
|
||||
// await nav
|
||||
// }
|
||||
//
|
||||
// readonly baseURL: string
|
||||
// readonly submitHydra: Locator
|
||||
// readonly forgotPasswordLink: Locator
|
||||
// readonly logoutLink: Locator
|
||||
//
|
||||
// async goto(returnTo?: string, refresh?: boolean) {
|
||||
// const u = new URL(routes.hosted.login(this.baseURL))
|
||||
// if (returnTo) {
|
||||
// u.searchParams.append("return_to", returnTo)
|
||||
// }
|
||||
// if (refresh) {
|
||||
// u.searchParams.append("refresh", refresh.toString())
|
||||
// }
|
||||
// await this.page.goto(u.toString())
|
||||
// await this.isReady()
|
||||
// }
|
||||
//
|
||||
// async loginWithHydra(email: string, password: string) {
|
||||
// await this.submitHydra.click()
|
||||
// await this.page.waitForURL(new RegExp(OIDC_PROVIDER))
|
||||
//
|
||||
// await this.page.locator("input[name=email]").fill(email)
|
||||
// await this.page.locator("input[name=password]").fill(password)
|
||||
//
|
||||
// await this.page.locator("input[name=submit][id=accept]").click()
|
||||
// }
|
||||
//
|
||||
// async loginWithOIDC(email = generateEmail(), password = generatePassword()) {
|
||||
// await this.page.fill('[name="email"]', email)
|
||||
// await this.page.fill('[name="password"]', password)
|
||||
// await this.page.click("#accept")
|
||||
// }
|
||||
//
|
||||
// async loginAndAcceptConsent(
|
||||
// email = generateEmail(),
|
||||
// password = generatePassword(),
|
||||
// {rememberConsent = true, rememberLogin = false} = {},
|
||||
// ) {
|
||||
// await this.page.fill('[name="email"]', email)
|
||||
// await this.page.fill('[name="password"]', password)
|
||||
// rememberLogin && (await this.page.check('[name="remember"]'))
|
||||
// await this.page.click("#accept")
|
||||
//
|
||||
// await this.page.click("#offline")
|
||||
// await this.page.click("#openid")
|
||||
// rememberConsent && (await this.page.check("[name=remember]"))
|
||||
// await this.page.click("#accept")
|
||||
//
|
||||
// return email
|
||||
// }
|
||||
//
|
||||
// async expectAlert(id: string) {
|
||||
// await this.page.getByTestId(`ui/message/${id}`).waitFor()
|
||||
// }
|
||||
}
|
||||
48
test/e2e/playwright/models/elements/registration.ts
Normal file
48
test/e2e/playwright/models/elements/registration.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright © 2024 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { expect, Page } from "@playwright/test"
|
||||
import { createInputLocator, InputLocator } from "../../selectors/input"
|
||||
import { OryKratosConfiguration } from "../../../shared/config"
|
||||
|
||||
export class RegistrationPage {
|
||||
public identifier: InputLocator
|
||||
|
||||
constructor(
|
||||
readonly page: Page,
|
||||
readonly config: OryKratosConfiguration,
|
||||
) {
|
||||
this.identifier = createInputLocator(page, "identifier")
|
||||
}
|
||||
|
||||
async open() {
|
||||
await Promise.all([
|
||||
this.page.goto(this.config.selfservice.flows.registration.ui_url),
|
||||
this.isReady(),
|
||||
this.page.waitForURL((url) =>
|
||||
url
|
||||
.toString()
|
||||
.includes(this.config.selfservice.flows.registration.ui_url),
|
||||
),
|
||||
])
|
||||
await this.isReady()
|
||||
}
|
||||
|
||||
inputField(name: string) {
|
||||
return this.page.locator(`input[name="${name}"]`)
|
||||
}
|
||||
|
||||
submitField(name: string) {
|
||||
return this.page.locator(`[type="submit"][name="method"][value="${name}"]`)
|
||||
}
|
||||
|
||||
async isReady() {
|
||||
await expect(this.inputField("csrf_token").nth(0)).toBeHidden()
|
||||
}
|
||||
|
||||
async triggerRegistrationWithCode(identifier: string) {
|
||||
await this.inputField("traits.phone").fill(identifier)
|
||||
await this.submitField("profile").click()
|
||||
await this.submitField("code").click()
|
||||
}
|
||||
}
|
||||
41
test/e2e/playwright/models/elements/settings.ts
Normal file
41
test/e2e/playwright/models/elements/settings.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2024 Ory Corp
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { expect, Page } from "@playwright/test"
|
||||
import { TOTP } from "otpauth"
|
||||
import { OryKratosConfiguration } from "../../../shared/config"
|
||||
|
||||
export class SettingsPage {
|
||||
constructor(
|
||||
readonly page: Page,
|
||||
readonly config: OryKratosConfiguration,
|
||||
) {}
|
||||
|
||||
async isReady() {
|
||||
for (const csrfInput of await this.page
|
||||
.locator(`input[name="csrf_token"]`)
|
||||
.all()) {
|
||||
await expect(csrfInput).toHaveValue(/.+/)
|
||||
}
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this.page.goto(this.config.selfservice.flows.settings.ui_url)
|
||||
await this.isReady()
|
||||
}
|
||||
|
||||
async setupTotp() {
|
||||
const totpSecret = await this.page
|
||||
.getByTestId("node/text/totp_secret_key/text")
|
||||
.locator("code")
|
||||
.textContent()
|
||||
await expect(totpSecret).toMatch(/^[A-Z2-7]{32}$/)
|
||||
const totpCode = () => {
|
||||
return new TOTP({ secret: totpSecret }).generate()
|
||||
}
|
||||
await this.page.fill("input[name=totp_code]", totpCode())
|
||||
await this.page.locator("button[name=method][value=totp]").click()
|
||||
|
||||
return { totpCode }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user