diff --git a/cypress/fixtures/okta-responses/success/idx-challenge-answer-password-enroll-email-response.json b/cypress/fixtures/okta-responses/success/idx-challenge-answer-password-enroll-email-response.json new file mode 100644 index 000000000..66c9fa74c --- /dev/null +++ b/cypress/fixtures/okta-responses/success/idx-challenge-answer-password-enroll-email-response.json @@ -0,0 +1,58 @@ +{ + "code": 200, + "response": { + "version": "1.0.0", + "stateHandle": "02.id.state~c.handle", + "expiresAt": "2099-12-31T23:59:59.000Z", + "remediation": { + "type": "array", + "value": [ + { + "rel": ["create-form"], + "name": "select-authenticator-enroll", + "href": "https://profile.code.dev-theguardian.com/idp/idx/credential/enroll", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "authenticator", + "type": "object", + "options": [ + { + "label": "Email", + "value": { + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "id", + "mutable": false + }, + { + "name": "methodType", + "required": false, + "value": "email", + "mutable": false + } + ] + } + }, + "relatesTo": "$.authenticators.value[0]" + } + ] + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + } + ] + } + } +} diff --git a/cypress/fixtures/okta-responses/success/idx-challenge-response-email.json b/cypress/fixtures/okta-responses/success/idx-challenge-response-email.json new file mode 100644 index 000000000..3af1eda66 --- /dev/null +++ b/cypress/fixtures/okta-responses/success/idx-challenge-response-email.json @@ -0,0 +1,229 @@ +{ + "code": 200, + "response": { + "version": "1.0.0", + "stateHandle": "02.id.state~c.handle", + "expiresAt": "2099-12-31T23:59:59.000Z", + "intent": "LOGIN", + "remediation": { + "type": "array", + "value": [ + { + "rel": ["create-form"], + "name": "challenge-authenticator", + "relatesTo": ["$.currentAuthenticatorEnrollment"], + "href": "https://profile.thegulocal.com/idp/idx/challenge/answer", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "credentials", + "type": "object", + "form": { + "value": [ + { + "name": "passcode", + "label": "Enter code" + } + ] + }, + "required": true + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + { + "rel": ["create-form"], + "name": "select-authenticator-authenticate", + "href": "https://profile.thegulocal.com/idp/idx/challenge", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "authenticator", + "type": "object", + "options": [ + { + "label": "Email", + "value": { + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "emailId", + "mutable": false + }, + { + "name": "methodType", + "required": false, + "value": "email", + "mutable": false + } + ] + } + }, + "relatesTo": "$.authenticatorEnrollments.value[0]" + } + ] + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + } + ] + }, + "currentAuthenticatorEnrollment": { + "type": "object", + "value": { + "profile": { + "email": "test@example.com" + }, + "resend": { + "rel": ["create-form"], + "name": "resend", + "href": "https://profile.thegulocal.com/idp/idx/challenge/resend", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "contextualData": { + "useEmailMagicLink": false + }, + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ] + } + }, + "authenticators": { + "type": "array", + "value": [ + { + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ], + "allowedFor": "any" + } + ] + }, + "authenticatorEnrollments": { + "type": "array", + "value": [ + { + "profile": { + "email": "g***7@gmail.com" + }, + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ] + } + ] + }, + "recoveryAuthenticator": { + "type": "object", + "value": { + "type": "password", + "key": "okta_password", + "id": "passwordId", + "displayName": "Password", + "methods": [ + { + "type": "password" + } + ] + } + }, + "user": { + "type": "object", + "value": { + "identifier": "test@example.com" + } + }, + "cancel": { + "rel": ["create-form"], + "name": "cancel", + "href": "https://profile.thegulocal.com/idp/idx/cancel", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "app": { + "type": "object", + "value": { + "name": "oidc_client", + "label": "sample_application", + "id": "clientId" + } + }, + "authentication": { + "type": "object", + "value": { + "protocol": "OAUTH2.0", + "issuer": { + "id": "issuerId", + "name": "Authorization Server", + "uri": "https://profile.thegulocal.com/oauth2/issuerId" + }, + "request": { + "max_age": -1, + "scope": "openid", + "response_type": "code", + "redirect_uri": "localhost", + "state": "state", + "code_challenge_method": "S256", + "code_challenge": "test", + "response_mode": "query" + } + } + } + } +} diff --git a/cypress/fixtures/okta-responses/success/idx-challenge-response-password.json b/cypress/fixtures/okta-responses/success/idx-challenge-response-password.json new file mode 100644 index 000000000..39a97e33f --- /dev/null +++ b/cypress/fixtures/okta-responses/success/idx-challenge-response-password.json @@ -0,0 +1,255 @@ +{ + "code": 200, + "response": { + "version": "1.0.0", + "stateHandle": "02.id.state~c.handle", + "expiresAt": "2099-12-31T23:59:59.000Z", + "intent": "LOGIN", + "remediation": { + "type": "array", + "value": [ + { + "rel": ["create-form"], + "name": "challenge-authenticator", + "relatesTo": ["$.currentAuthenticatorEnrollment"], + "href": "https://profile.thegulocal.com/idp/idx/challenge/answer", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "credentials", + "type": "object", + "form": { + "value": [ + { + "name": "passcode", + "label": "Password", + "secret": true + } + ] + }, + "required": true + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + { + "rel": ["create-form"], + "name": "select-authenticator-authenticate", + "href": "https://profile.thegulocal.com/idp/idx/challenge", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "authenticator", + "type": "object", + "options": [ + { + "label": "Email", + "value": { + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "emailId", + "mutable": false + }, + { + "name": "methodType", + "required": false, + "value": "email", + "mutable": false + } + ] + } + }, + "relatesTo": "$.authenticatorEnrollments.value[0]" + }, + { + "label": "Password", + "value": { + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "passwordId", + "mutable": false + }, + { + "name": "methodType", + "required": false, + "value": "password", + "mutable": false + } + ] + } + }, + "relatesTo": "$.authenticatorEnrollments.value[1]" + } + ] + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + } + ] + }, + "currentAuthenticatorEnrollment": { + "type": "object", + "value": { + "recover": { + "rel": ["create-form"], + "name": "recover", + "href": "https://profile.thegulocal.com/idp/idx/recover", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "type": "password", + "key": "okta_password", + "id": "passwordId", + "displayName": "Password", + "methods": [ + { + "type": "password" + } + ] + } + }, + "authenticators": { + "type": "array", + "value": [ + { + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ], + "allowedFor": "any" + }, + { + "type": "password", + "key": "okta_password", + "id": "passwordId", + "displayName": "Password", + "methods": [ + { + "type": "password" + } + ], + "allowedFor": "sso" + } + ] + }, + "authenticatorEnrollments": { + "type": "array", + "value": [ + { + "profile": { + "email": "test@example.com" + }, + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ] + }, + { + "type": "password", + "key": "okta_password", + "id": "passwordId", + "displayName": "Password", + "methods": [ + { + "type": "password" + } + ] + } + ] + }, + "user": { + "type": "object", + "value": { + "identifier": "test@example.com" + } + }, + "cancel": { + "rel": ["create-form"], + "name": "cancel", + "href": "https://profile.thegulocal.com/idp/idx/cancel", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "app": { + "type": "object", + "value": { + "name": "oidc_client", + "label": "sample_application", + "id": "clientId" + } + }, + "authentication": { + "type": "object", + "value": { + "protocol": "OAUTH2.0", + "issuer": { + "id": "issuerId", + "name": "Authorization Server", + "uri": "https://profile.thegulocal.com/oauth2/issuerId" + }, + "request": { + "max_age": -1, + "scope": "openid", + "response_type": "code", + "redirect_uri": "localhost", + "state": "state", + "code_challenge_method": "S256", + "code_challenge": "test", + "response_mode": "query" + } + } + } + } +} diff --git a/cypress/fixtures/okta-responses/success/idx-identify-response.ts b/cypress/fixtures/okta-responses/success/idx-identify-response.ts new file mode 100644 index 000000000..05f1acf22 --- /dev/null +++ b/cypress/fixtures/okta-responses/success/idx-identify-response.ts @@ -0,0 +1,206 @@ +export const identifyResponse = (email = true, password = true) => { + const emailOption = { + label: 'Email', + value: { + form: { + value: [ + { + name: 'id', + required: true, + value: 'emailId', + mutable: false, + }, + { + name: 'methodType', + required: false, + value: 'email', + mutable: false, + }, + ], + }, + }, + relatesTo: '$.authenticatorEnrollments.value[0]', + }; + + const passwordOption = { + label: 'Password', + value: { + form: { + value: [ + { + name: 'id', + required: true, + value: 'passwordId', + mutable: false, + }, + { + name: 'methodType', + required: false, + value: 'password', + mutable: false, + }, + ], + }, + }, + relatesTo: '$.authenticatorEnrollments.value[1]', + }; + + const options = [ + email ? emailOption : null, + password ? passwordOption : null, + ].filter(Boolean); + + const emailAuthenticator = { + type: 'email', + key: 'okta_email', + id: 'emailId', + displayName: 'Email', + methods: [ + { + type: 'email', + }, + ], + allowedFor: 'any', + }; + + const passwordAuthenticator = { + type: 'password', + key: 'okta_password', + id: 'passwordId', + displayName: 'Password', + methods: [ + { + type: 'password', + }, + ], + allowedFor: 'sso', + }; + + const authenticators = [ + email ? emailAuthenticator : null, + password ? passwordAuthenticator : null, + ].filter(Boolean); + + const emailEnrollment = { + profile: { + email: 'test@example.com', + }, + type: 'email', + key: 'okta_email', + id: 'emailFactorId', + displayName: 'Email', + methods: [ + { + type: 'email', + }, + ], + }; + + const passwordEnrollment = { + type: 'password', + key: 'okta_password', + id: 'passwordFactorId', + displayName: 'Password', + methods: [ + { + type: 'password', + }, + ], + }; + + const enrollments = [ + email ? emailEnrollment : null, + password ? passwordEnrollment : null, + ].filter(Boolean); + + return { + version: '1.0.0', + stateHandle: '02.id.state~c.handle', + expiresAt: '2099-12-31T23:59:59.000Z', + intent: 'LOGIN', + remediation: { + type: 'array', + value: [ + { + rel: ['create-form'], + name: 'select-authenticator-authenticate', + href: 'https://profile.thegulocal.com/idp/idx/challenge', + method: 'POST', + produces: 'application/ion+json; okta-version=1.0.0', + value: [ + { + name: 'authenticator', + type: 'object', + options, + }, + { + name: 'stateHandle', + required: true, + value: '02.id.state~c.handle', + visible: false, + mutable: false, + }, + ], + accepts: 'application/json; okta-version=1.0.0', + }, + ], + }, + authenticators, + authenticatorEnrollments: { + type: 'array', + value: enrollments, + }, + user: { + type: 'object', + value: { + identifier: 'test@example.com', + }, + }, + cancel: { + rel: ['create-form'], + name: 'cancel', + href: 'https://profile.thegulocal.com/idp/idx/cancel', + method: 'POST', + produces: 'application/ion+json; okta-version=1.0.0', + value: [ + { + name: 'stateHandle', + required: true, + value: '02.id.state~c.handle', + visible: false, + mutable: false, + }, + ], + accepts: 'application/json; okta-version=1.0.0', + }, + app: { + type: 'object', + value: { + name: 'oidc_client', + label: 'sample_application', + id: 'client_id', + }, + }, + authentication: { + type: 'object', + value: { + protocol: 'OAUTH2.0', + issuer: { + id: 'issuerId', + name: 'Authorization Server', + uri: 'https://profile.thegulocal.com/oauth2/issuerId', + }, + request: { + max_age: -1, + scope: 'openid', + response_type: 'code', + redirect_uri: 'localhost', + state: 'state', + code_challenge_method: 'S256', + code_challenge: 'test', + response_mode: 'query', + }, + }, + }, + }; +}; diff --git a/cypress/fixtures/okta-responses/success/idx-recover-response.json b/cypress/fixtures/okta-responses/success/idx-recover-response.json new file mode 100644 index 000000000..27bdb80b2 --- /dev/null +++ b/cypress/fixtures/okta-responses/success/idx-recover-response.json @@ -0,0 +1,238 @@ +{ + "code": 200, + "response": { + "version": "1.0.0", + "stateHandle": "02.id.state~c.handle", + "expiresAt": "2099-12-31T23:59:59.000Z", + "intent": "LOGIN", + "remediation": { + "type": "array", + "value": [ + { + "rel": ["create-form"], + "name": "authenticator-verification-data", + "relatesTo": ["$.currentAuthenticatorEnrollment"], + "href": "https://profile.thegulocal.com/idp/idx/challenge", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "authenticator", + "label": "Email", + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "emailId", + "mutable": false + }, + { + "name": "methodType", + "type": "string", + "required": true, + "options": [ + { + "label": "Email", + "value": "email" + } + ] + } + ] + } + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + { + "rel": ["create-form"], + "name": "select-authenticator-authenticate", + "href": "https://profile.thegulocal.com/idp/idx/challenge", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "authenticator", + "type": "object", + "options": [ + { + "label": "Email", + "value": { + "form": { + "value": [ + { + "name": "id", + "required": true, + "value": "emailId", + "mutable": false + }, + { + "name": "methodType", + "required": false, + "value": "email", + "mutable": false + } + ] + } + }, + "relatesTo": "$.authenticatorEnrollments.value[0]" + } + ] + }, + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + } + ] + }, + "currentAuthenticatorEnrollment": { + "type": "object", + "value": { + "profile": { + "email": "test@example.com" + }, + "resend": { + "rel": ["create-form"], + "name": "resend", + "href": "https://profile.thegulocal.com/idp/idx/challenge/resend", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ] + } + }, + "authenticators": { + "type": "array", + "value": [ + { + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ], + "allowedFor": "any" + } + ] + }, + "authenticatorEnrollments": { + "type": "array", + "value": [ + { + "profile": { + "email": "g***7@gmail.com" + }, + "type": "email", + "key": "okta_email", + "id": "emailId", + "displayName": "Email", + "methods": [ + { + "type": "email" + } + ] + } + ] + }, + "recoveryAuthenticator": { + "type": "object", + "value": { + "type": "password", + "key": "okta_password", + "id": "passwordId", + "displayName": "Password", + "methods": [ + { + "type": "password" + } + ] + } + }, + "user": { + "type": "object", + "value": { + "identifier": "test@example.com" + } + }, + "cancel": { + "rel": ["create-form"], + "name": "cancel", + "href": "https://profile.thegulocal.com/idp/idx/cancel", + "method": "POST", + "produces": "application/ion+json; okta-version=1.0.0", + "value": [ + { + "name": "stateHandle", + "required": true, + "value": "02.id.state~c.handle", + "visible": false, + "mutable": false + } + ], + "accepts": "application/json; okta-version=1.0.0" + }, + "app": { + "type": "object", + "value": { + "name": "oidc_client", + "label": "sample_application", + "id": "clientId" + } + }, + "authentication": { + "type": "object", + "value": { + "protocol": "OAUTH2.0", + "issuer": { + "id": "issuerId", + "name": "Authorization Server", + "uri": "https://profile.thegulocal.com/oauth2/issuerId" + }, + "request": { + "max_age": -1, + "scope": "openid", + "response_type": "code", + "redirect_uri": "localhost", + "state": "state", + "code_challenge_method": "S256", + "code_challenge": "test", + "response_mode": "query" + } + } + } + } +} diff --git a/cypress/integration/mocked/okta_send_reset_password.1.cy.ts b/cypress/integration/mocked/okta_send_reset_password.1.cy.ts deleted file mode 100644 index e8b38271e..000000000 --- a/cypress/integration/mocked/okta_send_reset_password.1.cy.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { UserResponse } from '@/server/models/okta/User'; -import updateUser from '../../fixtures/okta-responses/success/update-user.json'; - -describe('Send password reset email in Okta', () => { - const email = 'mrtest@theguardian.com'; - - const mockUserActiveWithPassword: UserResponse = { - id: 'test', - status: 'ACTIVE', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - password: {}, - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - const mockUserActiveWithoutPassword: UserResponse = { - id: 'test', - status: 'ACTIVE', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - const mockUserStaged: UserResponse = { - id: 'test', - status: 'STAGED', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - const mockUserProvisioned: UserResponse = { - id: 'test', - status: 'PROVISIONED', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - const mockUserRecovery: UserResponse = { - id: 'test', - status: 'RECOVERY', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - const mockUserPasswordExpired: UserResponse = { - id: 'test', - status: 'PASSWORD_EXPIRED', - profile: { - login: 'mrtest@theguardian.com', - email: 'mrtest@theguardian.com', - }, - credentials: { - provider: { - type: 'OKTA', - name: 'OKTA', - }, - }, - }; - - beforeEach(() => { - cy.mockPurge(); - }); - - context('send reset password email for ACTIVE user', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserActiveWithPassword); - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('send reset password email for ACTIVE user without password', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserActiveWithoutPassword); - cy.mockNext(403, { - errorCode: 'E0000006', - errorSummary: - 'You do not have permission to perform the requested action', - errorLink: 'E0000006', - errorId: 'errorId', - errorCauses: [], - }); - cy.mockNext(200, { - resetPasswordUrl: `https://${Cypress.env( - 'BASE_URI', - )}/reset_password/token_token_token_to`, - }); - cy.mockNext(200, { - stateToken: 'stateToken', - expiresAt: new Date(Date.now() + 1800000 /* 30min */), - status: 'SUCCESS', - _embedded: { - user: { - id: '12345', - passwordChanged: new Date().toISOString(), - profile: { - login: email, - firstName: null, - lastName: null, - }, - }, - }, - }); - cy.mockNext(200, { - expiresAt: new Date(Date.now() + 1800000 /* 30min */), - status: 'SUCCESS', - sessionToken: 'aValidSessionToken', - _embedded: { - user: { - id: '12345', - passwordChanged: new Date().toISOString(), - profile: { - login: email, - firstName: null, - lastName: null, - locale: 'en_US', - timeZone: 'America/Los_Angeles', - }, - }, - }, - }); - cy.mockNext(updateUser.code, updateUser.response); - cy.mockNext(200, mockUserActiveWithoutPassword); - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('send create password email for STAGED user', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserStaged); - cy.mockNext(200, { - activationUrl: `token_token_token_to`, - activationToken: `token_token_token_to`, - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('send create password email for PROVISIONED user', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserProvisioned); - cy.mockNext(200, { - activationUrl: `token_token_token_to`, - activationToken: `token_token_token_to`, - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('send reset password email for RECOVERY user', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserRecovery); - cy.mockNext(200, { - resetPasswordUrl: `https://${Cypress.env( - 'BASE_URI', - )}/reset_password/token_token_token_to`, - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('send reset password email for PASSWORD_EXPIRED user', () => { - it('shows email sent page when successful', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserPasswordExpired); - cy.mockNext(200, { - resetPasswordUrl: `https://${Cypress.env( - 'BASE_URI', - )}/reset_password/token_token_token_to`, - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('user not found', () => { - it('shows email sent page even when user not found', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(404, { - errorCode: 'E0000007', - errorSummary: 'User not found', - errorLink: 'E0000007', - errorId: 'errorId', - errorCauses: [], - }); - cy.get('button[type="submit"]').click(); - cy.contains('Check your inbox'); - cy.contains('email within 2 minutes'); - cy.contains('send again'); - }); - }); - - context('generic error handling', () => { - it('shows a generic error when something goes wrong', () => { - cy.visit('/reset-password'); - cy.get('input[name="email"]').type(email); - cy.mockNext(200, mockUserActiveWithPassword); - cy.mockNext(403, { - errorCode: 'E0000006', - errorSummary: - 'You do not have permission to perform the requested action', - errorLink: 'E0000006', - errorId: 'errorId', - errorCauses: [], - }); - cy.get('button[type="submit"]').click(); - cy.contains('Sorry, something went wrong. Please try again.'); - }); - }); -}); diff --git a/cypress/integration/mocked/resetPasswordController.4.cy.ts b/cypress/integration/mocked/resetPasswordController.4.cy.ts index 7180eab84..fc2758b73 100644 --- a/cypress/integration/mocked/resetPasswordController.4.cy.ts +++ b/cypress/integration/mocked/resetPasswordController.4.cy.ts @@ -8,10 +8,68 @@ import resetPasswordResponse from '../../fixtures/okta-responses/success/reset-p import verifyRecoveryTokenReponse from '../../fixtures/okta-responses/success/verify-recovery-token.json'; import authResetPasswordResponse from '../../fixtures/okta-responses/success/auth-reset-password.json'; import updateUser from '../../fixtures/okta-responses/success/update-user.json'; +import { identifyResponse } from '../../fixtures/okta-responses/success/idx-identify-response'; +import idxChallengeResponsePassword from '../../fixtures/okta-responses/success/idx-challenge-response-password.json'; +import idxChallengeResponseEmail from '../../fixtures/okta-responses/success/idx-challenge-response-email.json'; +import idxRecoverResponse from '../../fixtures/okta-responses/success/idx-recover-response.json'; +import idxChallengeAnswerPasswordEnrollEmailResponse from '../../fixtures/okta-responses/success/idx-challenge-answer-password-enroll-email-response.json'; +import idxInteractResponse from '../../fixtures/okta-responses/success/idx-interact-response.json'; +import idxIntrospectDefaultResponse from '../../fixtures/okta-responses/success/idx-introspect-default-response.json'; -beforeEach(() => { - cy.mockPurge(); -}); +export const baseIdxPasscodeResetPasswordMocks = () => { + // interact + cy.mockNext(idxInteractResponse.code, idxInteractResponse.response); + // introspect + cy.mockNext( + idxIntrospectDefaultResponse.code, + idxIntrospectDefaultResponse.response, + ); +}; + +export const dangerouslySetPlaceholderPasswordMocks = (email: string) => { + cy.mockNext(200, { + resetPasswordUrl: `https://${Cypress.env( + 'BASE_URI', + )}/reset_password/token_token_token_to`, + activationUrl: `token_token_token_to`, + activationToken: `token_token_token_to`, + }); + cy.mockNext(200, { + stateToken: 'stateToken', + expiresAt: new Date(Date.now() + 1800000 /* 30min */), + status: 'SUCCESS', + _embedded: { + user: { + id: '12345', + passwordChanged: new Date().toISOString(), + profile: { + login: email, + firstName: null, + lastName: null, + }, + }, + }, + }); + cy.mockNext(200, { + expiresAt: new Date(Date.now() + 1800000 /* 30min */), + status: 'SUCCESS', + sessionToken: 'aValidSessionToken', + _embedded: { + user: { + id: '12345', + passwordChanged: new Date().toISOString(), + profile: { + login: email, + firstName: null, + lastName: null, + locale: 'en_US', + timeZone: 'America/Los_Angeles', + }, + }, + }, + }); + cy.mockNext(updateUser.code, updateUser.response); +}; const verifyIn2MinutesEmailSentPage = () => { cy.contains('Check your inbox'); @@ -25,6 +83,12 @@ const verifyInRegularEmailSentPage = () => { cy.contains('We’ve sent an email'); cy.contains('within 2 minutes').should('not.exist'); }; +const verifyIn2MinutesEmailSentPagePasscodes = () => { + cy.contains('Enter your one-time code'); + cy.contains('send again'); + cy.contains('We’ve sent a 6-digit code'); + cy.contains('within 2 minutes'); +}; const setupMocksForSocialUserPasswordReset = () => { // Response from getUser() @@ -56,153 +120,871 @@ const setupMocksForSocialUserPasswordReset = () => { }); }; +const setupMocksForActiveUsersWithEmailPasswordFactors = (status: string) => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, identifyResponse(true, true)); + cy.mockNext( + idxChallengeResponsePassword.code, + idxChallengeResponsePassword.response, + ); + cy.mockNext(idxRecoverResponse.code, idxRecoverResponse.response); + cy.mockNext( + idxChallengeResponseEmail.code, + idxChallengeResponseEmail.response, + ); +}; + +const setupMocksForActiveUsersEmailFactorOnly = (status: string) => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, identifyResponse(true, false)); + dangerouslySetPlaceholderPasswordMocks('example@example.com'); + // function restart + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, identifyResponse(true, true)); + cy.mockNext( + idxChallengeResponsePassword.code, + idxChallengeResponsePassword.response, + ); + cy.mockNext(idxRecoverResponse.code, idxRecoverResponse.response); + cy.mockNext( + idxChallengeResponseEmail.code, + idxChallengeResponseEmail.response, + ); +}; + +const setupMocksForActiveUsersPasswordFactorOnly = (status: string) => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, identifyResponse(false, true)); + dangerouslySetPlaceholderPasswordMocks('example@example.com'); + cy.mockNext( + idxChallengeResponsePassword.code, + idxChallengeResponsePassword.response, + ); + cy.mockNext( + idxChallengeResponsePassword.code, + idxChallengeResponsePassword.response, + ); + cy.mockNext( + idxChallengeAnswerPasswordEnrollEmailResponse.code, + idxChallengeAnswerPasswordEnrollEmailResponse.response, + ); + cy.mockNext( + idxChallengeResponseEmail.code, + idxChallengeResponseEmail.response, + ); +}; + +const setupMocksForNonActiveUsers = (status: string) => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, {}); + dangerouslySetPlaceholderPasswordMocks('test@example.com'); + cy.mockNext(200, { ...userResponse.response, status: 'ACTIVE' }); + baseIdxPasscodeResetPasswordMocks(); + cy.mockNext(200, identifyResponse(true, true)); + cy.mockNext( + idxChallengeResponsePassword.code, + idxChallengeResponsePassword.response, + ); + cy.mockNext(idxRecoverResponse.code, idxRecoverResponse.response); + cy.mockNext( + idxChallengeResponseEmail.code, + idxChallengeResponseEmail.response, + ); +}; + +beforeEach(() => { + cy.mockPurge(); +}); + userStatuses.forEach((status) => { - context(`Given I am a ${status || 'nonexistent'} user`, () => { + context( + `Given I am a ${status || 'nonexistent'} user - useOktaClassic`, + () => { + context('When I submit the form on /reset-password', () => { + beforeEach(() => { + cy.visit(`/reset-password?useOktaClassic=true`); + cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /reset-password/email-sent', () => { + beforeEach(() => { + // We mock the encrypted state cookie here to trick the endpoint + // into thinking we've just gone through the preceeding flow. + // For readEncryptedStateCookie to succeed, it relies on a testing + // env variable to be set, otherwise it won't be able to read the cookie. + cy.setEncryptedStateCookie({ + email: 'example@example.com', + }); + cy.visit(`/reset-password/email-sent?useOktaClassic=true`); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /reset-password/resend', () => { + beforeEach(() => { + cy.visit(`/reset-password/resend?useOktaClassic=true`); + cy.get('input[name="email"]').type('example@example.com'); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /reset-password/expired', () => { + beforeEach(() => { + cy.visit(`/reset-password/expired?useOktaClassic=true`); + cy.get('input[name="email"]').type('example@example.com'); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /set-password/email-sent', () => { + beforeEach(() => { + // We mock the encrypted state cookie here to trick the endpoint + // into thinking we've just gone through the preceeding flow. + // For readEncryptedStateCookie to succeed, it relies on a testing + // env variable to be set, otherwise it won't be able to read the cookie. + cy.setEncryptedStateCookie({ + email: 'example@example.com', + }); + cy.visit(`/set-password/email-sent?useOktaClassic=true`); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /set-password/resend', () => { + beforeEach(() => { + cy.visit(`/set-password/resend?useOktaClassic=true`); + cy.get('input[name="email"]').type('example@example.com'); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + } + }); + context('When I submit the form on /set-password/expired', () => { + beforeEach(() => { + cy.visit(`/set-password/expired?useOktaClassic=true`); + cy.get('input[name="email"]').type('example@example.com'); + }); + switch (status) { + case false: + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'ACTIVE': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + // forgotPassword() + cy.mockNext(200, { + resetPasswordUrl: + 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', + }); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + specify( + "Then I should be shown the 'Check your inbox' page for social user", + () => { + setupMocksForSocialUserPasswordReset(); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'PROVISIONED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'STAGED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(tokenResponse.code, tokenResponse.response); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + case 'RECOVERY': + case 'PASSWORD_EXPIRED': + specify( + "Then I should be shown the 'Check your inbox' page", + () => { + // Set the correct user status on the response + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext( + resetPasswordResponse.code, + resetPasswordResponse.response, + ); + cy.get('button[type=submit]').click(); + verifyInRegularEmailSentPage(); + }, + ); + break; + } + }); + }, + ); + + context(`Given I am a ${status || 'nonexistent'} user - passcodes`, () => { context('When I submit the form on /reset-password', () => { beforeEach(() => { cy.visit(`/reset-password`); cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.get('button[type="submit"]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", + () => { + setupMocksForActiveUsersEmailFactorOnly(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", () => { - setupMocksForSocialUserPasswordReset(); + setupMocksForActiveUsersPasswordFactorOnly(status); cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); context('When I submit the form on /reset-password/email-sent', () => { beforeEach(() => { - // We mock the encrypted state cookie here to trick the endpoint - // into thinking we've just gone through the preceeding flow. - // For readEncryptedStateCookie to succeed, it relies on a testing - // env variable to be set, otherwise it won't be able to read the cookie. cy.setEncryptedStateCookie({ - email: 'example@example.com', + stateHandle: 'test-state-handle', + stateHandleExpiresAt: new Date( + Date.now() + 1000 * 60 * 30, + ).toISOString(), + email: 'test@example.com', }); cy.visit(`/reset-password/email-sent`); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", () => { - setupMocksForSocialUserPasswordReset(); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", + () => { + setupMocksForActiveUsersEmailFactorOnly(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", + () => { + setupMocksForActiveUsersPasswordFactorOnly(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.contains('send again').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); @@ -210,70 +992,75 @@ userStatuses.forEach((status) => { beforeEach(() => { cy.visit(`/reset-password/resend`); cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.get('button[type="submit"]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", + () => { + setupMocksForActiveUsersEmailFactorOnly(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", () => { - setupMocksForSocialUserPasswordReset(); + setupMocksForActiveUsersPasswordFactorOnly(status); cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); @@ -281,292 +1068,248 @@ userStatuses.forEach((status) => { beforeEach(() => { cy.visit(`/reset-password/expired`); cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); - break; - case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", () => { - setupMocksForSocialUserPasswordReset(); + cy.mockNext(userNotFoundError.code, userNotFoundError.response); cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; - case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); - break; - case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); - break; - case 'RECOVERY': - case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyIn2MinutesEmailSentPage(); - }); - break; - } - }); - context('When I submit the form on /set-password/email-sent', () => { - beforeEach(() => { - // We mock the encrypted state cookie here to trick the endpoint - // into thinking we've just gone through the preceeding flow. - // For readEncryptedStateCookie to succeed, it relies on a testing - // env variable to be set, otherwise it won't be able to read the cookie. - cy.setEncryptedStateCookie({ - email: 'example@example.com', - }); - cy.visit(`/set-password/email-sent`); - }); - switch (status) { - case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); - break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.get('button[type="submit"]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", + () => { + setupMocksForActiveUsersEmailFactorOnly(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", () => { - setupMocksForSocialUserPasswordReset(); + setupMocksForActiveUsersPasswordFactorOnly(status); cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); context('When I submit the form on /set-password/resend', () => { beforeEach(() => { - cy.visit(`/set-password/resend`); + cy.visit(`/set-password/reend`); cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.get('button[type="submit"]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", () => { - setupMocksForSocialUserPasswordReset(); + setupMocksForActiveUsersEmailFactorOnly(status); cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", + () => { + setupMocksForActiveUsersPasswordFactorOnly(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); - context('When I submit the form on /set-password/expired', () => { beforeEach(() => { cy.visit(`/set-password/expired`); cy.get('input[name="email"]').type('example@example.com'); + cy.setEncryptedStateCookie({}); }); switch (status) { case false: - specify("Then I should be shown the 'Check your inbox' page", () => { - cy.mockNext(userNotFoundError.code, userNotFoundError.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + cy.mockNext(userNotFoundError.code, userNotFoundError.response); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'ACTIVE': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - // forgotPassword() - cy.mockNext(200, { - resetPasswordUrl: - 'https://example.com/signin/reset-password/XE6wE17zmphl3KqAPFxO', - }); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); specify( - "Then I should be shown the 'Check your inbox' page for social user", + "Then I should be shown the 'Enter your one-time code' page", () => { - setupMocksForSocialUserPasswordReset(); + setupMocksForActiveUsersWithEmailPasswordFactors(status); + cy.get('button[type="submit"]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for social user", + () => { + setupMocksForActiveUsersEmailFactorOnly(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); + specify( + "Then I should be shown the 'Enter your one-time code' page for password only authenticator user", + () => { + setupMocksForActiveUsersPasswordFactorOnly(status); cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); + verifyIn2MinutesEmailSentPagePasscodes(); }, ); break; case 'PROVISIONED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'STAGED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext(tokenResponse.code, tokenResponse.response); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; case 'RECOVERY': case 'PASSWORD_EXPIRED': - specify("Then I should be shown the 'Check your inbox' page", () => { - // Set the correct user status on the response - const response = { ...userResponse.response, status }; - cy.mockNext(userResponse.code, response); - cy.mockNext( - resetPasswordResponse.code, - resetPasswordResponse.response, - ); - cy.get('button[type=submit]').click(); - verifyInRegularEmailSentPage(); - }); + specify( + "Then I should be shown the 'Enter your one-time code' page", + () => { + setupMocksForNonActiveUsers(status); + cy.get('button[type=submit]').click(); + verifyIn2MinutesEmailSentPagePasscodes(); + }, + ); break; } }); }); + + context(`generic error handling - ${status}`, () => { + it('shows a generic error when something goes wrong', () => { + cy.visit('/reset-password'); + cy.get('input[name="email"]').type('test@example.com'); + const response = { ...userResponse.response, status }; + cy.mockNext(userResponse.code, response); + cy.mockNext(403, { + errorCode: 'E0000006', + errorSummary: + 'You do not have permission to perform the requested action', + errorLink: 'E0000006', + errorId: 'errorId', + errorCauses: [], + }); + cy.get('button[type="submit"]').click(); + cy.contains('Sorry, something went wrong. Please try again.'); + }); + }); });