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

This commit is contained in:
upstream-mirror
2026-09-15 21:19:12 +05:00
commit c60cc7ca75
8092 changed files with 394160 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { MOBILE_URL, gen } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
context("Login error messages with code method", () => {
;[
{
route: express.login,
app: "express" as "express",
profile: "code",
},
{
route: react.login,
app: "react" as "react",
profile: "code",
},
{
route: MOBILE_URL + "/Login",
app: "mobile" as "mobile",
profile: "code",
},
].forEach(({ route, profile, app }) => {
describe(`for app ${app}`, () => {
const Selectors = {
mobile: {
identity: '[data-testid="identifier"]',
code: '[data-testid="code"]',
},
express: {
identity: '[data-testid="login-flow-code"] input[name="identifier"]',
code: 'input[name="code"]',
},
react: {
identity: 'input[name="identifier"]',
code: 'input[name="code"]',
},
}
before(() => {
if (app !== "mobile") {
cy.proxy(app)
}
})
beforeEach(() => {
cy.useConfigProfile(profile)
cy.deleteMail()
cy.clearAllCookies()
const email = gen.email()
cy.wrap(email).as("email")
cy.registerWithCode({ email, traits: { "traits.tos": 1 } })
cy.deleteMail()
cy.clearAllCookies()
cy.visit(route)
})
// playwright:migrated
it("should show error message when account identifier does not exist", () => {
const email = gen.email()
cy.get(Selectors[app]["identity"]).type(email)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4000035"]').should(
"contain",
"This account does not exist or has not setup sign in with code.",
)
})
// playwright:migrated
it("should show error message when code is invalid", () => {
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).clear().type(email.toString())
})
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1010014"]').should("exist")
cy.get(Selectors[app]["code"]).type("123456")
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4010008"]').should(
"contain",
"The login code is invalid or has already been used. Please try again.",
)
})
// playwright:migrated
it("should show error message when identifier has changed", () => {
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).type(email.toString())
})
cy.submitCodeForm(app)
if (app !== "express") {
cy.intercept("POST", "/self-service/login*", (req) => {
req.body = {
...req.body,
identifier: gen.email(),
}
req.continue()
}).as("login")
} else {
cy.get(Selectors[app]["identity"])
.type("{selectall}{backspace}", { force: true })
.type(gen.email(), { force: true })
}
cy.get(Selectors[app]["code"]).type("123456")
cy.submitCodeForm(app)
if (app !== "express") {
cy.wait("@login")
}
cy.get('[data-testid="ui/message/4000035"]').should(
"contain",
"This account does not exist or has not setup sign in with code.",
)
})
// playwright:migrated
it("should show error message when required fields are missing", () => {
cy.removeAttribute([Selectors[app]["identity"]], "required")
cy.submitCodeForm(app)
if (app === "mobile") {
cy.get('[data-testid="field/identifier"]').should(
"contain",
"Property identifier is missing",
)
} else {
cy.get('[data-testid="ui/message/4000002"]').should(
"contain",
"Property identifier is missing",
)
}
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).type(email.toString())
})
cy.submitCodeForm(app)
cy.removeAttribute([Selectors[app]["code"]], "required")
cy.submitCodeForm(app)
if (app === "mobile") {
cy.get('[data-testid="field/code"]').should(
"contain",
"Property code is missing",
)
} else {
cy.get('[data-testid="ui/message/4000002"]').should(
"contain",
"Property code is missing",
)
}
})
// playwright:migrated
it("should show error message when code is expired", () => {
cy.updateConfigFile((config) => {
config.selfservice.methods.code = {
passwordless_enabled: true,
config: {
lifespan: "1ns",
},
}
return config
}).then(() => {
cy.visit(route)
})
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).type(email.toString())
})
cy.submitCodeForm(app)
cy.get("@email").then((email) => {
cy.getLoginCodeFromEmail(email.toString()).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
})
})
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4010008"]').should(
"contain",
"The login code is invalid or has already been used",
)
cy.noSession()
})
})
})
})

View File

@@ -0,0 +1,332 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { Session } from "@ory/kratos-client"
import { gen, MOBILE_URL } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
context("Login success with code method", () => {
;[
{
route: express.login,
app: "express" as "express",
profile: "code",
},
{
route: react.login,
app: "react" as "react",
profile: "code",
},
{
route: MOBILE_URL + "/Login",
app: "mobile" as "mobile",
profile: "code",
},
].forEach(({ route, profile, app }) => {
describe(`for app ${app}`, () => {
const Selectors = {
mobile: {
identity: '[data-testid="identifier"]',
code: '[data-testid="code"]',
submit: '[data-testid="field/method/code"]',
resend: '[data-testid="field/resend/code"]',
},
express: {
identity: '[data-testid="login-flow-code"] input[name="identifier"]',
code: 'input[name="code"]',
submit: 'button[name="method"][value="code"]',
resend: 'button[name="resend"]',
},
react: {
identity: 'input[name="identifier"]',
code: 'input[name="code"]',
submit: 'button[name="method"][value="code"]',
resend: 'button[name="resend"]',
},
}
before(() => {
cy.deleteMail()
cy.useConfigProfile(profile)
if (app !== "mobile") {
cy.proxy(app)
}
cy.setPostCodeRegistrationHooks([])
cy.setupHooks("login", "after", "code", [])
})
beforeEach(() => {
const email = gen.email()
cy.wrap(email).as("email")
cy.registerWithCode({ email, traits: { "traits.tos": 1 } })
cy.deleteMail()
cy.clearAllCookies()
cy.visit(route)
})
// playwright:migrated
it("should be able to sign in with code", () => {
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).clear().type(email.toString())
cy.submitCodeForm(app)
cy.getLoginCodeFromEmail(email.toString()).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submit"]).click()
})
cy.location("pathname").should("not.contain", "login")
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal(
"completed",
)
expect(identity.traits.email).to.equal(email)
})
})
})
// playwright:migrated
it("should be able to sign in with code on account registered with password", () => {
const email = gen.email()
// register account with password
cy.register({
email,
password: gen.password(),
fields: { "traits.tos": 1 },
})
cy.getVerificationCodeFromEmail(email).then((code) => {
expect(code).to.not.be.empty
cy.deleteMail()
})
cy.clearAllCookies()
cy.visit(route)
cy.get(Selectors[app]["identity"]).clear().type(email)
cy.submitCodeForm(app)
cy.getLoginCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submit"]).click()
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should be able to resend login code", () => {
cy.get("@email").then((email) => {
cy.get(Selectors[app]["identity"]).clear().type(email.toString())
cy.submitCodeForm(app)
cy.getLoginCodeFromEmail(email.toString()).then((code) => {
cy.wrap(code).as("code1")
})
cy.get(Selectors[app]["resend"]).click()
cy.getLoginCodeFromEmail(email.toString()).then((code) => {
cy.wrap(code).as("code2")
})
cy.get("@code1").then((code1) => {
cy.get("@code2").then((code2) => {
expect(code1).to.not.equal(code2)
})
})
// attempt to submit code 1
cy.get("@code1").then((code1) => {
cy.get(Selectors[app]["code"]).clear().type(code1.toString())
})
cy.get(Selectors[app]["submit"]).click()
cy.get("[data-testid='ui/message/4010008']").contains(
"The login code is invalid or has already been used",
)
// attempt to submit code 2
cy.get("@code2").then((code2) => {
cy.get(Selectors[app]["code"]).clear().type(code2.toString())
})
cy.get(Selectors[app]["submit"]).click()
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "express") {
cy.get('a[href*="sessions"').click()
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then((session) => {
const { identity } = session
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal(
"completed",
)
expect(identity.traits.email).to.equal(email)
})
})
})
it("should be able to login to un-verfied email", () => {
const email = gen.email()
const email2 = gen.email()
// Setup complex schema
cy.setIdentitySchema(
"file://test/e2e/profiles/code/identity.complex.traits.schema.json",
)
cy.registerWithCode({
email: email,
traits: {
"traits.username": Math.random().toString(36),
"traits.email2": email2,
},
})
// There are verification emails from the registration process in the inbox that we need to deleted
// for the assertions below to pass.
cy.deleteMail({ atLeast: 1 })
cy.visit(route)
cy.get(Selectors[app]["identity"]).clear().type(email2)
cy.submitCodeForm(app)
cy.getLoginCodeFromEmail(email2).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submit"]).click()
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
cy.get('[data-testid="session-content"]').should("contain", email2)
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then((session) => {
expect(session?.identity?.verifiable_addresses).to.have.length(2)
expect(session?.identity?.verifiable_addresses[0].status).to.equal(
"completed",
)
expect(session.identity.verifiable_addresses[1].status).to.equal(
"completed",
)
})
})
})
})
})

View File

@@ -0,0 +1,197 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { UiNode } from "@ory/kratos-client"
import { gen, MOBILE_URL } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
context("Registration error messages with code method", () => {
;[
{
route: express.registration,
app: "express" as "express",
profile: "code",
},
{
route: react.registration,
app: "react" as "react",
profile: "code",
},
{
route: MOBILE_URL + "/Registration",
app: "mobile" as "mobile",
profile: "code",
},
].forEach(({ route, profile, app }) => {
describe(`for app ${app}`, () => {
const Selectors = {
mobile: {
identifier: "[data-testid='field/identifier']",
email: "[data-testid='field/traits.email']",
tos: "[data-testid='traits.tos']",
code: "[data-testid='field/code']",
},
express: {
identifier:
"[data-testid='registration-flow-code'] input[name='identifier']",
email:
"[data-testid='registration-flow-code'] input[name='traits.email']",
tos: "[data-testid='registration-flow-code'] [name='traits.tos'] + label",
code: "input[name='code']",
},
react: {
identifier: "input[name='identifier']",
email: "input[name='traits.email']",
tos: "[name='traits.tos'] + label",
code: "input[name='code']",
},
}
before(() => {
if (app !== "mobile") {
cy.proxy(app)
}
cy.useConfigProfile(profile)
cy.deleteMail()
})
beforeEach(() => {
cy.deleteMail()
cy.clearAllCookies()
cy.visit(route)
})
// playwright:migrated
it("should show error message when code is invalid", () => {
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.get(Selectors[app]["code"]).type("123456")
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4040003"]').should(
"contain",
"The registration code is invalid or has already been used. Please try again.",
)
})
// playwright:migrated
it("should show error message when traits have changed", () => {
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
if (app !== "express") {
// the mobile app doesn't render hidden fields in the DOM
// we need to replace the request body
cy.intercept("POST", "/self-service/registration*", (req) => {
req.body = {
...req.body,
"traits.email": "changed-email@email.com",
}
req.continue()
}).as("registration")
} else {
cy.get(Selectors[app]["email"])
.type("{selectall}{backspace}", { force: true })
.type("changed-email@email.com", { force: true })
}
cy.get(Selectors[app]["code"]).type("123456")
cy.submitCodeForm(app)
if (app !== "express") {
cy.wait("@registration")
}
cy.get('[data-testid="ui/message/4000036"]').should(
"contain",
"The provided traits do not match the traits previously associated with this flow.",
)
})
// playwright:migrated
it("should show error message when required fields are missing", () => {
cy.removeAttribute([Selectors[app]["email"]], "required")
const email = gen.email()
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
if (app === "mobile") {
cy.get('[data-testid="field/traits.email"]').should(
"contain",
"Property email is missing",
)
} else {
cy.get('[data-testid="ui/message/4000002"]').should(
"contain",
"Property email is missing",
)
}
cy.get(Selectors[app]["email"]).type(email)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.removeAttribute([Selectors[app]["code"]], "required")
cy.submitCodeForm(app)
if (app === "mobile") {
cy.get('[data-testid="field/code"]').should(
"contain",
"Property code is missing",
)
} else {
cy.get('[data-testid="ui/message/4000002"]').should(
"contain",
"Property code is missing",
)
}
})
// playwright:migrated
it("should show error message when code is expired", () => {
cy.updateConfigFile((config) => {
config.selfservice.methods.code.config.lifespan = "1ns"
return config
})
cy.visit(route)
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.submitCodeForm(app)
})
cy.get('[data-testid="ui/message/4040003"]').should(
"contain",
"The registration code is invalid or has already been used",
)
cy.noSession()
cy.updateConfigFile((config) => {
config.selfservice.methods.code.config.lifespan = "1h"
return config
})
})
})
})
})

View File

@@ -0,0 +1,418 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { Session } from "@ory/kratos-client"
import { gen, MOBILE_URL } from "../../../../helpers"
import { routes as express } from "../../../../helpers/express"
import { routes as react } from "../../../../helpers/react"
const Selectors = {
mobile: {
identifier: "[data-testid='field/identifier']",
recoveryEmail: "[data-testid='field/email']",
email: "[data-testid='traits.email']",
email2: "[data-testid='traits.email2']",
tos: "[data-testid='traits.tos']",
username: "[data-testid='traits.username']",
code: "[data-testid='field/code'] input",
recoveryCode: "[data-testid='code']",
submitCode: "[data-testid='field/method/code']",
resendCode: "[data-testid='field/resend/code']",
submitRecovery: "[data-testid='field/method/code']",
codeHiddenMethod: "[data-testid='field/method/code']",
},
express: {
identifier: "[data-testid='login-flow-code'] input[name='identifier']",
recoveryEmail: "input[name=email]",
email: "[data-testid='registration-flow-code'] input[name='traits.email']",
email2:
"[data-testid='registration-flow-code'] input[name='traits.email2']",
tos: "[data-testid='registration-flow-code'] [name='traits.tos'] + label",
username:
"[data-testid='registration-flow-code'] input[name='traits.username']",
code: "input[name='code']",
recoveryCode: "input[name=code]",
submitRecovery: "button[name=method][value=code]",
submitCode: "button[name='method'][value='code']",
resendCode: "button[name='resend'][value='code']",
codeHiddenMethod: "input[name='method'][value='code'][type='hidden']",
},
react: {
identifier: "input[name='identifier']",
recoveryEmail: "input[name=email]",
email: "input[name='traits.email']",
email2: "input[name='traits.email2']",
tos: "[name='traits.tos'] + label",
username: "input[name='traits.username']",
code: "input[name='code']",
recoveryCode: "input[name=code]",
submitRecovery: "button[name=method][value=code]",
submitCode: "button[name='method'][value='code']",
resendCode: "button[name='resend'][value='code']",
codeHiddenMethod: "input[name='method'][value='code'][type='hidden']",
},
}
context("Registration success with code method", () => {
;[
{
route: express.registration,
login: express.login,
recovery: express.recovery,
app: "express" as "express",
profile: "code",
},
{
route: react.registration,
login: react.login,
recovery: react.recovery,
app: "react" as "react",
profile: "code",
},
{
route: MOBILE_URL + "/Registration",
login: MOBILE_URL + "/Login",
recovery: MOBILE_URL + "/Recovery",
app: "mobile" as "mobile",
profile: "code",
},
].forEach(({ route, login, recovery, profile, app }) => {
describe(`for app ${app}`, () => {
before(() => {
cy.useConfigProfile(profile)
if (app !== "mobile") {
cy.proxy(app)
}
})
beforeEach(() => {
cy.deleteMail()
cy.clearAllCookies()
cy.visit(route)
})
// playwright:migrated
it("should be able to resend the registration code", () => {
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) =>
cy.wrap(code).as("code1"),
)
cy.get(Selectors[app]["codeHiddenMethod"]).should("exist")
cy.get(Selectors[app]["resendCode"]).click()
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.wrap(code).as("code2")
})
cy.get("@code1").then((code1) => {
// previous code should not work
cy.get(Selectors[app]["code"]).clear().type(code1.toString())
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/4040003"]').should(
"contain.text",
"The registration code is invalid or has already been used. Please try again.",
)
})
cy.get("@code2").then((code2) => {
cy.get(Selectors[app]["code"]).clear().type(code2.toString())
cy.submitCodeForm(app)
})
if (app === "mobile") {
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
cy.get('[data-testid="session-content"]').should("contain", email)
cy.get('[data-testid="session-token"]').should("not.be.empty")
} else {
// Wait for the post-registration UI to settle before querying
// /sessions/whoami. cy.request does not retry the HTTP call on a
// non-2xx status, so without this sync getSession races the
// browser's session-cookie persistence and intermittently 401s.
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should sign up and be logged in with session hook", () => {
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').should("not.be.empty")
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
// playwright:migrated
it("should be able to sign up without session hook", () => {
cy.setPostCodeRegistrationHooks([])
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
cy.get(Selectors[app]["tos"]).click()
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
cy.getRegistrationCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
cy.visit(login)
cy.get(Selectors[app]["identifier"]).type(email)
cy.get(Selectors[app]["submitCode"]).click()
cy.getLoginCodeFromEmail(email).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').should("not.be.empty")
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(1)
expect(identity.verifiable_addresses[0].status).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
it("should be able to recover account when registered with code", () => {
if (app === "mobile") {
cy.log("WARNING: skipping test for mobile app")
return
}
if (app === "react") {
// This test is flaky on React, so we skip it for now.
return
}
const email = gen.email()
cy.registerWithCode({ email, traits: { "traits.tos": 1 } })
cy.clearAllCookies()
cy.visit(recovery)
cy.get(Selectors[app]["recoveryEmail"]).type(email)
cy.get(Selectors[app]["submitRecovery"]).click()
cy.recoveryEmailWithCode({ expect: { email, enterCode: false } }).then(
() => {
cy.get<string>("@recoveryCode").then((code) => {
cy.get(Selectors[app]["recoveryCode"]).type(code)
})
},
)
cy.get(Selectors[app]["submitRecovery"]).click()
// Wait for the post-recovery settings page to render so the session
// cookie is in Cypress's jar before we hit /sessions/whoami.
cy.location("pathname").should("contain", "settings")
cy.getSession({ expectAal: "aal1" }).then((session) => {
cy.wrap(session).as("session")
})
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.traits.email).to.equal(email)
})
})
// Try keep this test as the last one, as it updates the identity schema.
it("should be able to use multiple identifiers to signup with and sign in to", () => {
cy.setPostCodeRegistrationHooks([
{
hook: "session",
},
{
hook: "show_verification_ui",
},
])
// Setup complex schema
cy.setIdentitySchema(
"file://test/e2e/profiles/code/identity.complex.traits.schema.json",
)
cy.visit(route)
cy.get(Selectors[app]["username"]).type(Math.random().toString(36))
const email = gen.email()
cy.get(Selectors[app]["email"]).type(email)
const email2 = gen.email()
cy.get(Selectors[app]["email2"]).type(email2)
cy.submitCodeForm(app)
cy.get('[data-testid="ui/message/1040005"]').should("be.visible")
// intentionally use email 1 to sign up for the account
cy.getRegistrationCodeFromEmail(email, { expectedCount: 1 }).then(
(code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
},
)
cy.get('[data-testid="ui/message/1080003"]').should("be.visible")
if (app === "mobile") {
cy.visit(MOBILE_URL + "/Home")
cy.get('*[data-testid="logout"]').click()
} else {
cy.logout()
}
// There are verification emails from the registration process in the inbox that we need to deleted
// for the assertions below to pass.
cy.deleteMail({ atLeast: 1 })
// Attempt to sign in with email 2 (should fail)
cy.visit(login)
cy.get(Selectors[app]["identifier"]).type(email2)
cy.get(Selectors[app]["submitCode"]).click()
cy.getLoginCodeFromEmail(email2, {
expectedCount: 1,
}).then((code) => {
cy.get(Selectors[app]["code"]).type(code)
cy.get(Selectors[app]["submitCode"]).click()
})
if (app === "express") {
cy.url().should("match", /\/welcome/)
} else {
cy.get('[data-testid="session-content"]').should("contain", email)
}
if (app === "mobile") {
cy.get('[data-testid="session-token"]').should("not.be.empty")
cy.get('[data-testid="session-token"]').then((token) => {
cy.getSession({
expectAal: "aal1",
expectMethods: ["code"],
token: token.text(),
}).then((session) => {
cy.wrap(session).as("session")
})
})
} else {
cy.getSession({ expectAal: "aal1", expectMethods: ["code"] }).then(
(session) => {
cy.wrap(session).as("session")
},
)
}
cy.get<Session>("@session").then(({ identity }) => {
expect(identity.id).to.not.be.empty
expect(identity.verifiable_addresses).to.have.length(2)
expect(
identity.verifiable_addresses.filter((v) => v.value === email)[0]
.status,
).to.equal("completed")
expect(
identity.verifiable_addresses.filter((v) => v.value === email2)[0]
.status,
).to.equal("completed")
expect(identity.traits.email).to.equal(email)
})
})
})
})
})