Create Cypress command to log in

I'll use this to test outfit saving!
This commit is contained in:
Emi Matchu 2021-04-19 02:16:31 -07:00
parent 9f2629ae61
commit 531ca3c22f
5 changed files with 113 additions and 22 deletions

View file

@ -0,0 +1,6 @@
describe("WardrobePage: Outfit saving", () => {
it("logs in", () => {
cy.logInAs("dti-test");
cy.visit("/");
});
});

View file

@ -1,6 +1,12 @@
require("dotenv").config();
const { initPlugin } = require("cypress-plugin-snapshots/plugin"); const { initPlugin } = require("cypress-plugin-snapshots/plugin");
module.exports = (on, config) => { module.exports = (on, config) => {
initPlugin(on, config); initPlugin(on, config);
config.env.AUTH0_TEST_CLIENT_ID = process.env.AUTH0_TEST_CLIENT_ID;
config.env.AUTH0_TEST_CLIENT_SECRET = process.env.AUTH0_TEST_CLIENT_SECRET;
config.env.DTI_TEST_USER_PASSWORD = process.env.DTI_TEST_USER_PASSWORD;
return config; return config;
}; };

View file

@ -1 +1,42 @@
import "cypress-plugin-snapshots/commands"; import "cypress-plugin-snapshots/commands";
import * as jwt from "jsonwebtoken";
// https://docs.cypress.io/guides/testing-strategies/auth0-authentication#Custom-Command-for-Auth0-Authentication
// Adapted and simplified by a lot though!
Cypress.Commands.add("logInAs", (username) => {
if (username !== "dti-test") {
throw new Error(`Cypress can only log in as dti-test right now`);
}
const password = Cypress.env("DTI_TEST_USER_PASSWORD");
const client_id = Cypress.env("AUTH0_TEST_CLIENT_ID");
const client_secret = Cypress.env("AUTH0_TEST_CLIENT_SECRET");
const audience = "https://impress-2020.openneo.net/api";
const scope = "";
// TODO: This grant doesn't seem to include the custom username field. The
// app is generally resilient to that, but yeah, it means the username
// might not show up in the global header UI during these tests.
cy.request({
method: "POST",
url: `https://openneo.us.auth0.com/oauth/token`,
body: {
grant_type: "password",
username,
password,
audience,
scope,
client_id,
client_secret,
},
}).then(({ body }) => {
const decodedUser = jwt.decode(body.access_token);
const auth0Cypress = {
encodedToken: body.access_token,
decodedUser,
};
window.localStorage.setItem("auth0Cypress", JSON.stringify(auth0Cypress));
});
});

View file

@ -3,6 +3,7 @@ import { setContext } from "@apollo/client/link/context";
import { createPersistedQueryLink } from "apollo-link-persisted-queries"; import { createPersistedQueryLink } from "apollo-link-persisted-queries";
import cachedZones from "./cached-data/zones.json"; import cachedZones from "./cached-data/zones.json";
import { readCypressLoginData } from "./components/useCurrentUser";
// Teach Apollo to load certain fields from the cache, to avoid extra network // Teach Apollo to load certain fields from the cache, to avoid extra network
// requests. This happens a lot - e.g. reusing data from item search on the // requests. This happens a lot - e.g. reusing data from item search on the
@ -142,8 +143,26 @@ const buildAuthLink = (getAuth0) =>
return; return;
} }
// Wait for auth0 to stop loading, so we can maybe get a token! We'll do const token = await getAccessToken(getAuth0);
// this hackily by checking every 100ms until it's true. if (token) {
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
}
});
async function getAccessToken(getAuth0) {
// Our Cypress tests store login data separately. Use it if available!
const cypressToken = readCypressLoginData()?.encodedToken;
if (cypressToken) {
return cypressToken;
}
// Otherwise, wait for auth0 to stop loading, so we can maybe get a token!
// We'll do this hackily by checking every 100ms until it's true.
await new Promise((resolve) => { await new Promise((resolve) => {
function check() { function check() {
if (getAuth0().isLoading) { if (getAuth0().isLoading) {
@ -158,14 +177,9 @@ const buildAuthLink = (getAuth0) =>
const { isAuthenticated, getAccessTokenSilently } = getAuth0(); const { isAuthenticated, getAccessTokenSilently } = getAuth0();
if (isAuthenticated) { if (isAuthenticated) {
const token = await getAccessTokenSilently(); const token = await getAccessTokenSilently();
return { return token;
headers: { }
...headers,
authorization: token ? `Bearer ${token}` : "",
},
};
} }
});
const initialCache = {}; const initialCache = {};
for (const zone of cachedZones) { for (const zone of cachedZones) {

View file

@ -24,17 +24,41 @@ function useCurrentUser() {
}; };
} }
// Additionally, our Cypress tests do an actual end-to-end login as a test
// user. Use that token if present!
const cypressUser = readCypressLoginData()?.decodedUser;
if (cypressUser) {
return { isLoading: false, isLoggedIn: true, ...getUserInfo(cypressUser) };
}
if (isLoading || !isAuthenticated) { if (isLoading || !isAuthenticated) {
return { isLoading, isLoggedIn: false, id: null, username: null }; return { isLoading, isLoggedIn: false, id: null, username: null };
} }
// NOTE: Users created correctly should have these attributes... but I'm return { isLoading, isLoggedIn: true, ...getUserInfo(user) };
// coding defensively, because third-party integrations are always a }
// bit of a thing, and I don't want failures to crash us!
const id = user.sub?.match(/^auth0\|impress-([0-9]+)$/)?.[1];
const username = user["https://oauth.impress-2020.openneo.net/username"];
return { isLoading, isLoggedIn: true, id, username }; export function readCypressLoginData() {
const cypressUserJsonString = window.localStorage.getItem("auth0Cypress");
if (!cypressUserJsonString) {
return null;
}
try {
return JSON.parse(cypressUserJsonString);
} catch (e) {
console.warn(
"Could not parse auth0Cypress token in localStorage; ignoring."
);
return null;
}
}
function getUserInfo(user) {
return {
id: user.sub?.match(/^auth0\|impress-([0-9]+)$/)?.[1],
username: user["https://oauth.impress-2020.openneo.net/username"],
};
} }
export default useCurrentUser; export default useCurrentUser;