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
270 lines
7.8 KiB
TypeScript
270 lines
7.8 KiB
TypeScript
// 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()
|
|
// }
|
|
}
|