From e46f2c32e30613221124d1338da33cd7d3e402cd Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:34:30 +0900 Subject: [PATCH 1/7] Add Descope doc to testing strategies --- .../descope-authentication.mdx | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx new file mode 100644 index 0000000000..2e69e55a08 --- /dev/null +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -0,0 +1,310 @@ +--- +title: Descope Authentication +slug: /guides/end-to-end-testing/descope-authentication +--- + +:::info + +## What you'll learn + +- Log in to [Descope](https://descope.com) through the UI +- Programmatically authenticate with [Descope](https://descope.com) via a custom + Cypress command +- Adapt your [Descope](https://descope.com) application for programmatic + authentication during testing + +::: + +## Descope Application Setup + +To get started with Descope, an application needs to be setup within the +[Descope Dashboard](https://app.descope.com/home) via the following steps: + +1. Visit the [Descope Dashboard](https://app.descope.com/home) and click the + "+ Project" button under the project dropdown. +2. Enter the desired name for your application. +3. Hit the "Create" button + +## Setting Descope app credentials in Cypress + +To have access to test user credentials within our tests we need to configure +Cypress to use the [Descope](https://descope.com) environment variables set in the +`.env` file. + +:::cypress-config-example + +```js +// Populate process.env with values from .env file +require('dotenv').config() +``` + +```js +{ + e2e: { + includeShadowDom: true, // For interacting with Descope components + }, + env: { + descope_project_id: process.env.REACT_APP_DESCOPE_PROJECT_ID, + descope_management_key: process.env.REACT_APP_DESCOPE_MANAGEMENT_KEY + }, +} +``` + +::: + +## Custom Command for Descope Authentication + +There are two ways you can authenticate to Descope: + +- [Login with UI](#Login-with-UI) +- [Programmatic Login](#Programmatic-Login) + +## Initialize test user +For both UI and programatic login, you'll need to initialize a test user. + +```js +const projectId = Cypress.env('descope_project_id') +const managementKey = Cypress.env('descope_management_key') +const descopeAPIDomain = "api.descope.com" + +// Define the authorization header +const authHeader = { + 'Authorization': `Bearer ${projectId}:${managementKey}`, +} + +// Define the base URL for Descope API +const descopeApiBaseURL = `https://${descopeAPIDomain}/v1`; + +const testUserLoginId = "testUser" + Math.floor(1000 + Math.random() * 9000) + "@gmail.com"; // Must match email to pass validation + +// Define the test user details +const testUser = { + loginId: testUserLoginId, + email: testUserLoginId, + phone: "+11231231234", + verifiedEmail: true, + verifiedPhone: true, + displayName: "Test User", + test: true, +} +``` + +### Login with UI + +Next, we'll write a custom command called `loginToDescope` to perform a login to +[Descope](https://descope.com) using the [Test User Management API](https://docs.descope.com/api/testusermanagement/) + and navigating via the user interface. This command will + +1. Navigate to the Descope login +2. Input user credentials +3. Sign in +4. Cache the results with [`cy.session()`](/api/commands/session) + +```js +// cypress/support/auth-provider-commands/descope.ts + +function loginViaDescopeUi() { + // App landing page redirects to Auth0. + cy.visit('/') + + // Login on Auth0. + cy.origin( + Cypress.env('auth0_domain'), + { args: { username, password } }, + ({ username, password }) => { + cy.get('input#username').type(username) + cy.get('input#password').type(password, { log: false }) + cy.contains('button[value=default]', 'Continue').click() + } + ) + + // Ensure Auth0 has redirected us back to the RWA. + cy.url().should('equal', 'http://localhost:3000/') +} + +Cypress.Commands.add('loginToAuth0', () => { + const log = Cypress.log({ + displayName: 'Descope LOGIN', + message: [`🔐 Authenticating | ${username}`], + // @ts-ignore + autoEnd: false, + }) + log.snapshot('before') + + loginViaAuth0Ui(username, password) + + log.snapshot('after') + log.end() +}) +``` + +Now, we can use our `loginToAuth0` command in the test. Below is our test to +login as a user via Auth0 and run a basic sanity check. + +:::tip + +The +[runnable version of this test](https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/auth0.spec.ts) +is in the +[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app). + +::: + +```js +describe('Auth0', function () { + beforeEach(function () { + cy.task('db:seed') + cy.intercept('POST', '/graphql').as('createBankAccount') + cy.loginToAuth0( + Cypress.env('auth0_username'), + Cypress.env('auth0_password') + ) + cy.visit('/') + }) + + it('shows onboarding', function () { + cy.contains('Get Started').should('be.visible') + }) +}) +``` + + + +Lastly, we can refactor our login command to take advantage of +[`cy.session()`](/api/commands/session) to store our logged in user so we don't +have to reauthenticate before every test. + +```js +Cypress.Commands.add('loginToAuth0', (username: string, password: string) => { + const log = Cypress.log({ + displayName: 'AUTH0 LOGIN', + message: [`🔐 Authenticating | ${username}`], + // @ts-ignore + autoEnd: false, + }) + log.snapshot('before') + + cy.session( + `auth0-${username}`, + () => { + loginViaAuth0Ui(username, password) + }, + { + validate: () => { + // Validate presence of access token in localStorage. + cy.wrap(localStorage) + .invoke('getItem', 'authAccessToken') + .should('exist') + }, + } + ) + + log.snapshot('after') + log.end() +}) +``` + + + +### Programmatic Login + +Below is a command to programmatically login into [Auth0](https://auth0.com), +using the +[/oauth/token endpoint](https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint) +and set an item in `localStorage` with the authenticated users details, which we +will use in our application code to verify we are authenticated under test. + +The `loginByAuth0Api` command will execute the following steps: + +1. Use the + [/oauth/token endpoint](https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint) + to perform the programmatic login. +2. Finally the `auth0Cypress` `localStorage` item is set with the + `access token`, `id_token` and user profile. + +```jsx +// cypress/support/commands.js +Cypress.Commands.add( + 'loginByAuth0Api', + (username: string, password: string) => { + cy.log(`Logging in as ${username}`) + const client_id = Cypress.env('auth0_client_id') + const client_secret = Cypress.env('auth0_client_secret') + const audience = Cypress.env('auth0_audience') + const scope = Cypress.env('auth0_scope') + + cy.request({ + method: 'POST', + url: `https://${Cypress.env('auth0_domain')}/oauth/token`, + body: { + grant_type: 'password', + username, + password, + audience, + scope, + client_id, + client_secret, + }, + }).then(({ body }) => { + const claims = jwt.decode(body.id_token) + const { + nickname, + name, + picture, + updated_at, + email, + email_verified, + sub, + exp, + } = claims + + const item = { + body: { + ...body, + decodedToken: { + claims, + user: { + nickname, + name, + picture, + updated_at, + email, + email_verified, + sub, + }, + audience, + client_id, + }, + }, + expiresAt: exp, + } + + window.localStorage.setItem('auth0Cypress', JSON.stringify(item)) + + cy.visit('/') + }) + } +) +``` + +With our Auth0 app setup properly in the Auth0 Developer console, necessary +environment variables in place, and our `loginByAuth0Api` command implemented, +we will be able to authenticate with Auth0 while our app is under test. Below is +a test to login as a user via [Auth0](https://auth0.com), complete the +onboarding process and logout. + +```jsx +describe('Auth0', function () { + beforeEach(function () { + cy.task('db:seed') + cy.loginByAuth0Api( + Cypress.env('auth0_username'), + Cypress.env('auth0_password') + ) + }) + + it('shows onboarding', function () { + cy.contains('Get Started').should('be.visible') + }) +}) +``` From 4144e91feb1ed8d0c39d95f8c2a6899d8b7865ad Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Sun, 9 Jul 2023 19:50:16 +0900 Subject: [PATCH 2/7] Update descope-authentication.mdx --- .../testing-strategies/descope-authentication.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx index 2e69e55a08..daa488bd7b 100644 --- a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -10,8 +10,6 @@ slug: /guides/end-to-end-testing/descope-authentication - Log in to [Descope](https://descope.com) through the UI - Programmatically authenticate with [Descope](https://descope.com) via a custom Cypress command -- Adapt your [Descope](https://descope.com) application for programmatic - authentication during testing ::: @@ -168,8 +166,6 @@ describe('Auth0', function () { }) ``` - - Lastly, we can refactor our login command to take advantage of [`cy.session()`](/api/commands/session) to store our logged in user so we don't have to reauthenticate before every test. @@ -204,8 +200,6 @@ Cypress.Commands.add('loginToAuth0', (username: string, password: string) => { }) ``` - - ### Programmatic Login Below is a command to programmatically login into [Auth0](https://auth0.com), From 5f2ff02387387c8a42c094740a0e2f2ef8a56118 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:24:16 +0900 Subject: [PATCH 3/7] Update descope-authentication.mdx --- .../descope-authentication.mdx | 312 ++++++++---------- 1 file changed, 136 insertions(+), 176 deletions(-) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx index daa488bd7b..58d74b6756 100644 --- a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -57,10 +57,13 @@ There are two ways you can authenticate to Descope: - [Login with UI](#Login-with-UI) - [Programmatic Login](#Programmatic-Login) -## Initialize test user -For both UI and programatic login, you'll need to initialize a test user. +### Initialize test user & variables +For both UI and programatic login, you'll need to initialize a test user and get your env variables. +We'll do this in the `cypress/support/commands.js` file. ```js +// cypress/support/commands.js + const projectId = Cypress.env('descope_project_id') const managementKey = Cypress.env('descope_management_key') const descopeAPIDomain = "api.descope.com" @@ -87,218 +90,175 @@ const testUser = { } ``` +We’ll also need to clean up the created testing users before starting so we don’t go over the limit. +This is done with the `deleteAllTestUsers` function. +```js +// cypress/support/commands.js + +Cypress.Commands.add('deleteAllTestUsers', () => { + cy.request({ + method: 'DELETE', + url: `${descopeApiBaseURL}/mgmt/user/test/delete/all`, + headers: authHeader, + }) +}) +``` + + ### Login with UI -Next, we'll write a custom command called `loginToDescope` to perform a login to +Next, we'll write a custom command called `loginViaDescopeUI` to perform a login to [Descope](https://descope.com) using the [Test User Management API](https://docs.descope.com/api/testusermanagement/) and navigating via the user interface. This command will 1. Navigate to the Descope login -2. Input user credentials -3. Sign in -4. Cache the results with [`cy.session()`](/api/commands/session) +2. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the login (create user and generate OTP code). +3. Enter the user loginId and code that we just generated to log in via the user interface. ```js -// cypress/support/auth-provider-commands/descope.ts - -function loginViaDescopeUi() { - // App landing page redirects to Auth0. - cy.visit('/') - - // Login on Auth0. - cy.origin( - Cypress.env('auth0_domain'), - { args: { username, password } }, - ({ username, password }) => { - cy.get('input#username').type(username) - cy.get('input#password').type(password, { log: false }) - cy.contains('button[value=default]', 'Continue').click() - } - ) - - // Ensure Auth0 has redirected us back to the RWA. - cy.url().should('equal', 'http://localhost:3000/') -} - -Cypress.Commands.add('loginToAuth0', () => { - const log = Cypress.log({ - displayName: 'Descope LOGIN', - message: [`🔐 Authenticating | ${username}`], - // @ts-ignore - autoEnd: false, - }) - log.snapshot('before') - - loginViaAuth0Ui(username, password) +// cypress/support/commands.js - log.snapshot('after') - log.end() +Cypress.Commands.add('loginViaDescopeUI', () => { + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/user/create`, + headers: authHeader, + body: testUser, + }) + .then(({ body }) => { + const loginId = body["user"]["loginIds"][0]; + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, + headers: authHeader, + body: { + "loginId": loginId, + "deliveryMethod": "email" + } + }) + .then(({ body }) => { + const otpCode = body["code"] + const loginID = body["loginId"] + cy.visit('/login') + cy.get('descope-wc') + .find('input') + .type(loginID) + cy.get('descope-wc') + .find('button').contains('Continue').click() + cy.get('descope-wc').find('.descope-input-wrapper').find('input').should('exist') // Assertion added to wait for the OTP code input to appear + let otpCodeArray = Array.from(otpCode); // Convert the OTP code string to an array + for (var i = 0; i < otpCodeArray.length; i++) { + cy.get('descope-wc').find('.descope-input-wrapper').find('input').eq(i + 1).type(otpCodeArray[i], { force: true }) + } + cy.get('descope-wc') + .find('button').contains('Submit').click() + + // Customize these steps based on your authentication flow + }) + }) }) ``` -Now, we can use our `loginToAuth0` command in the test. Below is our test to -login as a user via Auth0 and run a basic sanity check. - -:::tip - -The -[runnable version of this test](https://github.com/cypress-io/cypress-realworld-app/blob/develop/cypress/tests/ui-auth-providers/auth0.spec.ts) -is in the -[Cypress Real World App](https://github.com/cypress-io/cypress-realworld-app). - -::: +Now, we can use our `loginViaDescopeUI` command in the test. Below is our test to +login as a user via Descope and run a basic sanity check. ```js -describe('Auth0', function () { +describe('Descope', function () { beforeEach(function () { - cy.task('db:seed') - cy.intercept('POST', '/graphql').as('createBankAccount') - cy.loginToAuth0( - Cypress.env('auth0_username'), - Cypress.env('auth0_password') - ) - cy.visit('/') + cy.deleteAllTestUsers() + cy.loginViaDescopeUI() + cy.visit('/') }) - it('shows onboarding', function () { - cy.contains('Get Started').should('be.visible') + it('shows welcome page', function () { + cy.contains('Welcome').should('be.visible') }) }) ``` -Lastly, we can refactor our login command to take advantage of -[`cy.session()`](/api/commands/session) to store our logged in user so we don't -have to reauthenticate before every test. - -```js -Cypress.Commands.add('loginToAuth0', (username: string, password: string) => { - const log = Cypress.log({ - displayName: 'AUTH0 LOGIN', - message: [`🔐 Authenticating | ${username}`], - // @ts-ignore - autoEnd: false, - }) - log.snapshot('before') - - cy.session( - `auth0-${username}`, - () => { - loginViaAuth0Ui(username, password) - }, - { - validate: () => { - // Validate presence of access token in localStorage. - cy.wrap(localStorage) - .invoke('getItem', 'authAccessToken') - .should('exist') - }, - } - ) - - log.snapshot('after') - log.end() -}) -``` - ### Programmatic Login -Below is a command to programmatically login into [Auth0](https://auth0.com), -using the -[/oauth/token endpoint](https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint) +We'll now write a command to programmatically login into +[Descope](https://descope.com) using the [Test User Management API](https://docs.descope.com/api/testusermanagement/) and set an item in `localStorage` with the authenticated users details, which we will use in our application code to verify we are authenticated under test. -The `loginByAuth0Api` command will execute the following steps: +The `loginViaDescopeApi` command will execute the following steps: + +1. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the programmatic login (create user, generate OTP code, and verify OTP code). +2. Set the `refreshToken` and `sessionToken` items in localStorage. -1. Use the - [/oauth/token endpoint](https://auth0.com/docs/protocols/protocol-oauth2#token-endpoint) - to perform the programmatic login. -2. Finally the `auth0Cypress` `localStorage` item is set with the - `access token`, `id_token` and user profile. ```jsx // cypress/support/commands.js -Cypress.Commands.add( - 'loginByAuth0Api', - (username: string, password: string) => { - cy.log(`Logging in as ${username}`) - const client_id = Cypress.env('auth0_client_id') - const client_secret = Cypress.env('auth0_client_secret') - const audience = Cypress.env('auth0_audience') - const scope = Cypress.env('auth0_scope') - +Cypress.Commands.add('loginViaDescopeAPI', () => { cy.request({ - method: 'POST', - url: `https://${Cypress.env('auth0_domain')}/oauth/token`, - body: { - grant_type: 'password', - username, - password, - audience, - scope, - client_id, - client_secret, - }, - }).then(({ body }) => { - const claims = jwt.decode(body.id_token) - const { - nickname, - name, - picture, - updated_at, - email, - email_verified, - sub, - exp, - } = claims - - const item = { - body: { - ...body, - decodedToken: { - claims, - user: { - nickname, - name, - picture, - updated_at, - email, - email_verified, - sub, - }, - audience, - client_id, - }, - }, - expiresAt: exp, - } - - window.localStorage.setItem('auth0Cypress', JSON.stringify(item)) - - cy.visit('/') + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/user/create`, + headers: authHeader, + body: testUser, }) - } -) + .then(({ body }) => { + const loginId = body["user"]["loginIds"][0]; + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, + headers: authHeader, + body: { + "loginId": loginId, + "deliveryMethod": "email" + } + }) + .then(({ body }) => { + const otpCode = body["code"] + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/auth/otp/verify/email`, + headers: authHeader, + body: { + "loginId": loginId, + "code": otpCode + } + }) + .then(({ body }) => { + const sessionJwt = body["sessionJwt"] + const refreshJwt = body["refreshJwt"] + + /** Default name for the session cookie name / local storage key */ + const SESSION_TOKEN_KEY = 'DS'; + /** Default name for the refresh local storage key */ + const REFRESH_TOKEN_KEY = 'DSR'; + + // // Store the JWT in the browser's local storage. + cy.window().then((win) => { + win.localStorage.setItem(SESSION_TOKEN_KEY, sessionJwt); + win.localStorage.setItem(REFRESH_TOKEN_KEY, refreshJwt); + }); + + // // Now navigate to the root URL of your application. + cy.visit('/') + + }) + }) + }) +}) ``` -With our Auth0 app setup properly in the Auth0 Developer console, necessary -environment variables in place, and our `loginByAuth0Api` command implemented, -we will be able to authenticate with Auth0 while our app is under test. Below is -a test to login as a user via [Auth0](https://auth0.com), complete the -onboarding process and logout. +With our Descope app setup properly in the Descope Developer console, +necessary environment variables in place, and our +`loginViaDescopeApi` command implemented, we will be able to authenticate + with Descope while our app is under test. Below is + a test to login as a user using our loginViaDescopeAPI function and verify the welcome page is showing. ```jsx -describe('Auth0', function () { +describe('Descope', function () { beforeEach(function () { - cy.task('db:seed') - cy.loginByAuth0Api( - Cypress.env('auth0_username'), - Cypress.env('auth0_password') - ) + cy.deleteAllTestUsers() + cy.loginViaDescopeAPI() }) - it('shows onboarding', function () { - cy.contains('Get Started').should('be.visible') - }) + it('shows welcome page', function () { + cy.contains('Welcome').should('be.visible') + }) }) ``` From c2d433b0f7218150ce64992fb8956fd130473d0f Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:24:29 +0900 Subject: [PATCH 4/7] Update descope-authentication.mdx --- .../testing-strategies/descope-authentication.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx index 58d74b6756..a61e5f2a6b 100644 --- a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -90,7 +90,7 @@ const testUser = { } ``` -We’ll also need to clean up the created testing users before starting so we don’t go over the limit. +We’ll also need to clean up the created test users before starting so we don’t go over the limit. This is done with the `deleteAllTestUsers` function. ```js // cypress/support/commands.js From e7107ed57b68a9dffdeca98632591aaa08e907e7 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:27:48 +0900 Subject: [PATCH 5/7] Update descope-authentication.mdx --- .../descope-authentication.mdx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx index a61e5f2a6b..b3b74715d5 100644 --- a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -7,7 +7,7 @@ slug: /guides/end-to-end-testing/descope-authentication ## What you'll learn -- Log in to [Descope](https://descope.com) through the UI +- Log in to [Descope](https://descope.com) via the UI - Programmatically authenticate with [Descope](https://descope.com) via a custom Cypress command @@ -58,7 +58,7 @@ There are two ways you can authenticate to Descope: - [Programmatic Login](#Programmatic-Login) ### Initialize test user & variables -For both UI and programatic login, you'll need to initialize a test user and get your env variables. +For both UI and programatic login, you'll need to initialize a test user, get your env variables, and create a delete users command. We'll do this in the `cypress/support/commands.js` file. ```js @@ -90,8 +90,8 @@ const testUser = { } ``` -We’ll also need to clean up the created test users before starting so we don’t go over the limit. -This is done with the `deleteAllTestUsers` function. +To clean up the created test users so we don’t go over the test user limit, +we'll have a `deleteAllTestUsers` function. ```js // cypress/support/commands.js @@ -113,7 +113,7 @@ Next, we'll write a custom command called `loginViaDescopeUI` to perform a login 1. Navigate to the Descope login 2. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the login (create user and generate OTP code). -3. Enter the user loginId and code that we just generated to log in via the user interface. +3. Enter the user login ID and code that we just generated to log in via the user interface. ```js // cypress/support/commands.js @@ -183,7 +183,7 @@ We'll now write a command to programmatically login into and set an item in `localStorage` with the authenticated users details, which we will use in our application code to verify we are authenticated under test. -The `loginViaDescopeApi` command will execute the following steps: +The `loginViaDescopeAPI` command will execute the following steps: 1. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the programmatic login (create user, generate OTP code, and verify OTP code). 2. Set the `refreshToken` and `sessionToken` items in localStorage. @@ -246,9 +246,9 @@ Cypress.Commands.add('loginViaDescopeAPI', () => { With our Descope app setup properly in the Descope Developer console, necessary environment variables in place, and our -`loginViaDescopeApi` command implemented, we will be able to authenticate +`loginViaDescopeAPI` command implemented, we will be able to authenticate with Descope while our app is under test. Below is - a test to login as a user using our loginViaDescopeAPI function and verify the welcome page is showing. + a test to login as a user using our `loginViaDescopeAPI` function and verify the welcome page is showing. ```jsx describe('Descope', function () { From 369ab58fb237a5468302f51e378d86f0b9614229 Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 10 Jul 2023 18:37:44 +0900 Subject: [PATCH 6/7] Make page "Prettier" --- .../descope-authentication.mdx | 212 +++++++++--------- 1 file changed, 105 insertions(+), 107 deletions(-) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx index b3b74715d5..e7c2b47dc1 100644 --- a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx +++ b/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx @@ -58,7 +58,8 @@ There are two ways you can authenticate to Descope: - [Programmatic Login](#Programmatic-Login) ### Initialize test user & variables -For both UI and programatic login, you'll need to initialize a test user, get your env variables, and create a delete users command. + +For both UI and programatic login, you'll need to initialize a test user, get your env variables, and create a delete users command. We'll do this in the `cypress/support/commands.js` file. ```js @@ -66,50 +67,51 @@ We'll do this in the `cypress/support/commands.js` file. const projectId = Cypress.env('descope_project_id') const managementKey = Cypress.env('descope_management_key') -const descopeAPIDomain = "api.descope.com" +const descopeAPIDomain = 'api.descope.com' // Define the authorization header const authHeader = { - 'Authorization': `Bearer ${projectId}:${managementKey}`, + Authorization: `Bearer ${projectId}:${managementKey}`, } // Define the base URL for Descope API -const descopeApiBaseURL = `https://${descopeAPIDomain}/v1`; +const descopeApiBaseURL = `https://${descopeAPIDomain}/v1` -const testUserLoginId = "testUser" + Math.floor(1000 + Math.random() * 9000) + "@gmail.com"; // Must match email to pass validation +const testUserLoginId = + 'testUser' + Math.floor(1000 + Math.random() * 9000) + '@gmail.com' // Must match email to pass validation // Define the test user details const testUser = { - loginId: testUserLoginId, - email: testUserLoginId, - phone: "+11231231234", - verifiedEmail: true, - verifiedPhone: true, - displayName: "Test User", - test: true, + loginId: testUserLoginId, + email: testUserLoginId, + phone: '+11231231234', + verifiedEmail: true, + verifiedPhone: true, + displayName: 'Test User', + test: true, } ``` To clean up the created test users so we don’t go over the test user limit, we'll have a `deleteAllTestUsers` function. + ```js // cypress/support/commands.js Cypress.Commands.add('deleteAllTestUsers', () => { - cy.request({ - method: 'DELETE', - url: `${descopeApiBaseURL}/mgmt/user/test/delete/all`, - headers: authHeader, - }) + cy.request({ + method: 'DELETE', + url: `${descopeApiBaseURL}/mgmt/user/test/delete/all`, + headers: authHeader, + }) }) ``` - ### Login with UI Next, we'll write a custom command called `loginViaDescopeUI` to perform a login to [Descope](https://descope.com) using the [Test User Management API](https://docs.descope.com/api/testusermanagement/) - and navigating via the user interface. This command will +and navigating via the user interface. This command will 1. Navigate to the Descope login 2. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the login (create user and generate OTP code). @@ -119,43 +121,44 @@ Next, we'll write a custom command called `loginViaDescopeUI` to perform a login // cypress/support/commands.js Cypress.Commands.add('loginViaDescopeUI', () => { + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/user/create`, + headers: authHeader, + body: testUser, + }).then(({ body }) => { + const loginId = body['user']['loginIds'][0] cy.request({ - method: 'POST', - url: `${descopeApiBaseURL}/mgmt/user/create`, - headers: authHeader, - body: testUser, + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, + headers: authHeader, + body: { + loginId: loginId, + deliveryMethod: 'email', + }, + }).then(({ body }) => { + const otpCode = body['code'] + const loginID = body['loginId'] + cy.visit('/login') + cy.get('descope-wc').find('input').type(loginID) + cy.get('descope-wc').find('button').contains('Continue').click() + cy.get('descope-wc') + .find('.descope-input-wrapper') + .find('input') + .should('exist') // Assertion added to wait for the OTP code input to appear + let otpCodeArray = Array.from(otpCode) // Convert the OTP code string to an array + for (var i = 0; i < otpCodeArray.length; i++) { + cy.get('descope-wc') + .find('.descope-input-wrapper') + .find('input') + .eq(i + 1) + .type(otpCodeArray[i], { force: true }) + } + cy.get('descope-wc').find('button').contains('Submit').click() + + // Customize these steps based on your authentication flow }) - .then(({ body }) => { - const loginId = body["user"]["loginIds"][0]; - cy.request({ - method: 'POST', - url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, - headers: authHeader, - body: { - "loginId": loginId, - "deliveryMethod": "email" - } - }) - .then(({ body }) => { - const otpCode = body["code"] - const loginID = body["loginId"] - cy.visit('/login') - cy.get('descope-wc') - .find('input') - .type(loginID) - cy.get('descope-wc') - .find('button').contains('Continue').click() - cy.get('descope-wc').find('.descope-input-wrapper').find('input').should('exist') // Assertion added to wait for the OTP code input to appear - let otpCodeArray = Array.from(otpCode); // Convert the OTP code string to an array - for (var i = 0; i < otpCodeArray.length; i++) { - cy.get('descope-wc').find('.descope-input-wrapper').find('input').eq(i + 1).type(otpCodeArray[i], { force: true }) - } - cy.get('descope-wc') - .find('button').contains('Submit').click() - - // Customize these steps based on your authentication flow - }) - }) + }) }) ``` @@ -167,7 +170,7 @@ describe('Descope', function () { beforeEach(function () { cy.deleteAllTestUsers() cy.loginViaDescopeUI() - cy.visit('/') + cy.visit('/') }) it('shows welcome page', function () { @@ -178,7 +181,7 @@ describe('Descope', function () { ### Programmatic Login -We'll now write a command to programmatically login into +We'll now write a command to programmatically login into [Descope](https://descope.com) using the [Test User Management API](https://docs.descope.com/api/testusermanagement/) and set an item in `localStorage` with the authenticated users details, which we will use in our application code to verify we are authenticated under test. @@ -188,67 +191,62 @@ The `loginViaDescopeAPI` command will execute the following steps: 1. Use the [Test User Management API](https://docs.descope.com/api/testusermanagement/) to perform the programmatic login (create user, generate OTP code, and verify OTP code). 2. Set the `refreshToken` and `sessionToken` items in localStorage. - ```jsx // cypress/support/commands.js Cypress.Commands.add('loginViaDescopeAPI', () => { + cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/user/create`, + headers: authHeader, + body: testUser, + }).then(({ body }) => { + const loginId = body['user']['loginIds'][0] cy.request({ + method: 'POST', + url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, + headers: authHeader, + body: { + loginId: loginId, + deliveryMethod: 'email', + }, + }).then(({ body }) => { + const otpCode = body['code'] + cy.request({ method: 'POST', - url: `${descopeApiBaseURL}/mgmt/user/create`, + url: `${descopeApiBaseURL}/auth/otp/verify/email`, headers: authHeader, - body: testUser, - }) - .then(({ body }) => { - const loginId = body["user"]["loginIds"][0]; - cy.request({ - method: 'POST', - url: `${descopeApiBaseURL}/mgmt/tests/generate/otp`, - headers: authHeader, - body: { - "loginId": loginId, - "deliveryMethod": "email" - } - }) - .then(({ body }) => { - const otpCode = body["code"] - cy.request({ - method: 'POST', - url: `${descopeApiBaseURL}/auth/otp/verify/email`, - headers: authHeader, - body: { - "loginId": loginId, - "code": otpCode - } - }) - .then(({ body }) => { - const sessionJwt = body["sessionJwt"] - const refreshJwt = body["refreshJwt"] - - /** Default name for the session cookie name / local storage key */ - const SESSION_TOKEN_KEY = 'DS'; - /** Default name for the refresh local storage key */ - const REFRESH_TOKEN_KEY = 'DSR'; - - // // Store the JWT in the browser's local storage. - cy.window().then((win) => { - win.localStorage.setItem(SESSION_TOKEN_KEY, sessionJwt); - win.localStorage.setItem(REFRESH_TOKEN_KEY, refreshJwt); - }); - - // // Now navigate to the root URL of your application. - cy.visit('/') - - }) - }) + body: { + loginId: loginId, + code: otpCode, + }, + }).then(({ body }) => { + const sessionJwt = body['sessionJwt'] + const refreshJwt = body['refreshJwt'] + + /** Default name for the session cookie name / local storage key */ + const SESSION_TOKEN_KEY = 'DS' + /** Default name for the refresh local storage key */ + const REFRESH_TOKEN_KEY = 'DSR' + + // // Store the JWT in the browser's local storage. + cy.window().then((win) => { + win.localStorage.setItem(SESSION_TOKEN_KEY, sessionJwt) + win.localStorage.setItem(REFRESH_TOKEN_KEY, refreshJwt) }) + + // // Now navigate to the root URL of your application. + cy.visit('/') + }) + }) + }) }) ``` -With our Descope app setup properly in the Descope Developer console, -necessary environment variables in place, and our +With our Descope app setup properly in the Descope Developer console, +necessary environment variables in place, and our `loginViaDescopeAPI` command implemented, we will be able to authenticate - with Descope while our app is under test. Below is - a test to login as a user using our `loginViaDescopeAPI` function and verify the welcome page is showing. +with Descope while our app is under test. Below is +a test to login as a user using our `loginViaDescopeAPI` function and verify the welcome page is showing. ```jsx describe('Descope', function () { @@ -259,6 +257,6 @@ describe('Descope', function () { it('shows welcome page', function () { cy.contains('Welcome').should('be.visible') - }) + }) }) ``` From 7e0fb6276a8885fc7fecf2d595d1be2f4aa3ca6d Mon Sep 17 00:00:00 2001 From: Allen Zhou <46854522+allenzhou101@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:19:21 -0600 Subject: [PATCH 7/7] Move Descope Authentication Guide to App folder --- .../guides/authentication-testing}/descope-authentication.mdx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{guides/end-to-end-testing/testing-strategies => app/guides/authentication-testing}/descope-authentication.mdx (100%) diff --git a/docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx b/docs/app/guides/authentication-testing/descope-authentication.mdx similarity index 100% rename from docs/guides/end-to-end-testing/testing-strategies/descope-authentication.mdx rename to docs/app/guides/authentication-testing/descope-authentication.mdx