cypress-io / cypress-realworld-app

A payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows.

Home Page:https://docs.cypress.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

cy.loginByOktaApi is not a function

qhu1234 opened this issue · comments

I followed the entire tutorial to try to make the okta authentication from here: https://docs.cypress.io/guides/end-to-end-testing/okta-authentication. And I also cloned this cypress-realworld-app to follow the exact same steps of how it sets up the okta.ts and the unit test file. I'd say the tutorial has the steps that are way different than how the repo sets up. The tutorial doesn't mention that you need to create a file named global.d.ts and add custom functions in there and some other places you need to make it work.

So here's what i have done:

  1. I created okta.ts in cypress-> support -> auth-provider-commands
    `// @ts-check
    ///

const loginToOkta = (username: string, password: string) => {
Cypress.log({
displayName: "OKTA LOGIN",
message: [🔐 Authenticating | ${username}],
autoEnd: false,
});

cy.visit("/");
cy.origin(Cypress.env("okta_domain"), { args: { username, password } }, ({ username, password }) => {
cy.get('input[name="identifier"]').type(username);
cy.get('input[name="credentials.passcode"]').type(password, {
log: false,
});
cy.get('[type="submit"]').click();
});

cy.get('[data-test="sidenav-username"]').should("contain", username);
};
Cypress.Commands.add("loginByOkta", (username: string, password: string) => {
cy.session(
okta-${username},
() => {
return loginToOkta(username, password);
},
{
validate() {
cy.visit("/");
cy.get('[data-test="sidenav-username"]').should("contain", username);
},
},
);
});
// Okta
Cypress.Commands.add("loginByOktaApi", (username, password) => {
const optionsSessionToken = {
method: 'POST',
url: 'https://newscorp.okta.com/api/v1/authn',
body: {
username: username,
password: password,
options: {
warnBeforePasswordExpired: 'true'
}
}
};

//first cy.request you need to get a OKTA session token
cy.request(optionsSessionToken).then(resp => {
const sessionToken = resp.body.sessionToken;
//once you have a token, you need to hit okta authorize URL with client ID and redirect URL.
//it will be a very long string with different params. Add a session token at the end of the string as parameter
cy.request({
method: 'GET',
url:
'https://your_company_link_to_authorize.com/oauth2/default/v1/authorize?client_id=11111111&redirect_uri=http://localhost:4200/callback&response_type=id_token token&OTHER PARAMS PARAMS PARAMS&sessionToken=' + sessionToken,
form: true,
followRedirect: false
}).then(respWithToken => {
const url = respWithToken.redirectedToUrl;
if (url) {
const token = (url as string)
.substring(url.indexOf('access_token'))
.split('=')[1]
.split('&')[0];
cy.wrap(token).as('token');
//last step is to visit the redirecturl and then visit homepage of the app
cy.visit(url).then(() => {
cy.visit('/');
});
}
});
});
});
2. I created the unit test file in cypress-> tests and I have to update mycypress.config.tsto change the spec file to read from tests folder instead of the defaulte2efolder:import { applicationConfig } from "../../src/config";
import { Config } from "../../src/config/common";

describe("ETRD page testing", () => {
let config: Config = applicationConfig;
beforeEach(function() {
cy.loginByOktaApi(Cypress.env("okta_username"), Cypress.env("okta_password"));
//cy.loginByOkta(Cypress.env("okta_username"), Cypress.env("okta_password"));
cy.visit("/");
});

it("Visits ETRD main page to check if content loads", () => {
cy.visit(config.externalServices.etrdDevPortal.href);
});
});
3. I created global.d.ts and added this code in: ///

declare namespace Cypress {
interface Chainable {
/**
* Window object with additional properties used during test.
*/
window(options?: Partial<Loggable & Timeoutable>): Chainable;

/**
 * Logs-in user by using Okta API request
 */
loginByOktaApi(username: string, password?: string): Chainable<Response>;

/**
 * Logs-in user by navigating to Okta tenant with cy.origin()
 */
loginByOkta(username: string, password: string): Chainable<Response>;

}
}
4. I also created cypress.d.ts in the root directory:import { mount } from "cypress/react";

// Augment the Cypress namespace to include type definitions for
// your custom command.
// Alternatively, can be defined in cypress/support/component.d.ts
// with a at the top of your spec.
declare global {
namespace Cypress {
interface Chainable {
mount: typeof mount;
}
}
}
In this cypress-realworld-app, the only 3 places I can findloginByOktaApiorloginByOktaareglobal.t.ds,okta.tsandokta.spec.ts`

When I run yarn cypress:open and navigate to the spec testing page it's still saying cy.loginByOktaApi is not a function.
Screenshot 2023-12-13 at 8 50 27 PM

It's already taking me 2 days to make the function work what else do I miss to make it work?
I use VS code, MacOS ventura and just upgraded to Sonoma.
What else do I need to set up to have the application recognize the custom function?

OMG I been trying to figure it out for 3 days and finally figured out.
I read the document here: https://docs.cypress.io/api/cypress-api/custom-commands#Command-Logging it says"
To support sideEffects:false, you can wrap the Cypress commands in a function that will be imported by the support file."
I don't have sideEffects:false defined in my package.json file but I tried to move my loginByOktaApi custom function in cypress-> support -> command.ts and wrap it with export function registerCommands() {...} and in my e2e.ts I pasted these 2 lines
import { registerCommands } from './commands' registerCommands()
and it finally registered the custom function.

I'm glad you figured this out, @qhu1234 ! Closing this issue as resolved.