Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into EVG-20806-2
Browse files Browse the repository at this point in the history
  • Loading branch information
khelif96 committed Sep 18, 2023
2 parents ac9fe1a + 74015b7 commit 5c5fe81
Show file tree
Hide file tree
Showing 41 changed files with 716 additions and 634 deletions.
7 changes: 7 additions & 0 deletions __mocks__/focus-trap-react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Mock focus-trap-react to prevent errors in tests that use modals. focus-trap-react is a package used
// by LeafyGreen and is not a direct dependency of Spruce.
const lib = jest.requireActual("focus-trap-react");

lib.prototype.setupFocusTrap = () => null;

module.exports = lib;
16 changes: 16 additions & 0 deletions __mocks__/tabbable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// https://github.com/focus-trap/tabbable#testing-in-jsdom
const lib = jest.requireActual("tabbable");

const tabbable = {
...lib,
tabbable: (node, options) =>
lib.tabbable(node, { ...options, displayCheck: "none" }),
focusable: (node, options) =>
lib.focusable(node, { ...options, displayCheck: "none" }),
isFocusable: (node, options) =>
lib.isFocusable(node, { ...options, displayCheck: "none" }),
isTabbable: (node, options) =>
lib.isTabbable(node, { ...options, displayCheck: "none" }),
};

module.exports = tabbable;
27 changes: 13 additions & 14 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,35 @@ import path from "path";

export const getConfig = ({
generatedFileName,
schema,
silent,
}: {
schema: string;
generatedFileName: string;
}): CodegenConfig => ({
schema,
} & Pick<CodegenConfig, "silent">): CodegenConfig => ({
documents: ["./src/**/*.ts", "./src/**/*.graphql", "./src/**/*.gql"].map(
(d) => path.resolve(__dirname, d)
),
hooks: {
afterAllFileWrite: [
`${path.resolve(__dirname, "./node_modules/.bin/prettier")} --write`,
],
},
overwrite: true,
generates: {
[generatedFileName]: {
plugins: ["typescript", "typescript-operations"],
config: {
preResolveTypes: true,
arrayInputCoercion: false,
preResolveTypes: true,
scalars: {
Duration: "number",
StringMap: "{ [key: string]: any }",
Time: "Date",
Duration: "number",
},
},
plugins: ["typescript", "typescript-operations"],
},
},
hooks: {
afterAllFileWrite: [
`${path.resolve(__dirname, "./node_modules/.bin/prettier")} --write`,
],
},
overwrite: true,
schema: "sdlschema/**/*.graphql",
silent,
});

export const generatedFileName = path.resolve(
Expand All @@ -40,6 +40,5 @@ export const generatedFileName = path.resolve(
);

export default getConfig({
schema: "sdlschema/**/*.graphql",
generatedFileName,
});
12 changes: 0 additions & 12 deletions config/jest/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,3 @@ window.crypto.randomUUID = (() => {
};
})();

// Mock focus-trap-react to prevent errors in tests that use modals. focus-trap-react is a package used
// by LeafyGreen and is not a direct dependency of Spruce.
jest.mock(
"focus-trap-react",
() => {
const focusTrap = jest.requireActual(
"focus-trap-react"
);
focusTrap.prototype.setupFocusTrap = () => null;
return focusTrap;
}
);
2 changes: 2 additions & 0 deletions cypress/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const EVG_BASE_URL = "http://localhost:9090";
export const GQL_URL = `${EVG_BASE_URL}/graphql/query`;
49 changes: 49 additions & 0 deletions cypress/integration/distroSettings/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,53 @@ describe("using the distro dropdown", () => {
cy.location("pathname").should("not.contain", "localhost");
cy.location("pathname").should("contain", "rhel71-power8-large");
});

describe("warning modal", () => {
it("warns when navigating away from distro settings with unsaved changes and allows returning to distro settings", () => {
cy.getInputByLabel("Notes").type("my note");
cy.dataCy("save-settings-button").should(
"not.have.attr",
"aria-disabled",
"true"
);
cy.dataCy("project-health-link").click();
cy.dataCy("navigation-warning-modal").should("be.visible");
cy.contains("button", "Cancel").click();
cy.dataCy("navigation-warning-modal").should("not.exist");
cy.location("pathname").should(
"eq",
"/distro/localhost/settings/general"
);
});

describe("modifying the distro provider", () => {
beforeEach(() => {
cy.visit("/distro/localhost/settings/provider");
});

it("warns when navigating to another distro settings tab after the provider has changed and allows save", () => {
cy.selectLGOption("Provider", "Docker");
cy.dataCy("save-settings-button").should(
"not.have.attr",
"aria-disabled",
"true"
);
cy.contains("a", "Task Settings").click();
cy.dataCy("save-modal").should("be.visible");
cy.dataCy("provider-warning-banner").should("be.visible");
});

it("shows the standard save warning modal when non-provider fields have changed", () => {
cy.getInputByLabel("User Data").type("test user data");
cy.dataCy("save-settings-button").should(
"not.have.attr",
"aria-disabled",
"true"
);
cy.dataCy("project-health-link").click();
cy.dataCy("navigation-warning-modal").should("be.visible");
cy.dataCy("provider-warning-banner").should("not.exist");
});
});
});
});
58 changes: 46 additions & 12 deletions cypress/integration/nav_bar.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { EVG_BASE_URL } from "../constants";

const PATCH_ID = "5e4ff3abe3c3317e352062e4";
const USER_ID = "admin";
const SPRUCE_URLS = {
Expand All @@ -6,19 +8,22 @@ const SPRUCE_URLS = {
cli: `/preferences/cli`,
};
const LEGACY_URLS = {
version: `/version/${PATCH_ID}`,
userPatches: `/patches/user/${USER_ID}`,
distros: `/distros`,
version: `${EVG_BASE_URL}/version/${PATCH_ID}`,
userPatches: `${EVG_BASE_URL}/patches/user/${USER_ID}`,
distros: `${EVG_BASE_URL}/distros`,
admin: `${EVG_BASE_URL}/admin`,
};
describe("Nav Bar", () => {
const projectCookie = "mci-project-cookie";

it("Should have a nav bar linking to the proper page on the legacy UI", () => {
cy.visit(SPRUCE_URLS.version);
cy.dataCy("legacy-ui-link").should("exist");
cy.dataCy("legacy-ui-link")
.should("have.attr", "href")
.and("include", LEGACY_URLS.version);
cy.dataCy("legacy-ui-link").should(
"have.attr",
"href",
LEGACY_URLS.version
);
});
it("Navigating to a different page should change the nav link to the legacy UI", () => {
cy.visit(SPRUCE_URLS.version);
Expand All @@ -28,9 +33,11 @@ describe("Nav Bar", () => {
.and("include", LEGACY_URLS.version);
cy.visit(SPRUCE_URLS.userPatches);
cy.dataCy("legacy-ui-link").should("exist");
cy.dataCy("legacy-ui-link")
.should("have.attr", "href")
.and("include", LEGACY_URLS.userPatches);
cy.dataCy("legacy-ui-link").should(
"have.attr",
"href",
LEGACY_URLS.userPatches
);
});
it("Visiting a page with no legacy equivalent should not display a nav link", () => {
cy.visit(SPRUCE_URLS.cli);
Expand All @@ -41,9 +48,7 @@ describe("Nav Bar", () => {
cy.dataCy("legacy_route").should("not.exist");
cy.dataCy("auxiliary-dropdown-link").click();
cy.dataCy("legacy_route").should("exist");
cy.dataCy("legacy_route")
.should("have.attr", "href")
.and("include", LEGACY_URLS.distros);
cy.dataCy("legacy_route").should("have.attr", "href", LEGACY_URLS.distros);
});
it("Nav Dropdown should link to patches page of most recent project if cookie exists", () => {
cy.setCookie(projectCookie, "spruce");
Expand Down Expand Up @@ -100,4 +105,33 @@ describe("Nav Bar", () => {
);
cy.getCookie(projectCookie).should("have.property", "value", "spruce");
});

describe("Admin settings", () => {
it("Should not show Admin button to non-admins", () => {
const userData = {
data: {
user: {
userId: "admin",
displayName: "Evergreen Admin",
emailAddress: "[email protected]",
permissions: {
canEditAdminSettings: false,
},
},
},
};
cy.overwriteGQL("User", userData);
cy.visit(SPRUCE_URLS.version);
cy.dataCy("user-dropdown-link").click();
cy.dataCy("admin-link").should("not.exist");
});

it("Should show Admin button to admins", () => {
cy.visit(SPRUCE_URLS.version);
cy.dataCy("user-dropdown-link").click();
cy.dataCy("admin-link")
.should("be.visible")
.should("have.attr", "href", LEGACY_URLS.admin);
});
});
});
2 changes: 1 addition & 1 deletion cypress/integration/version/action_buttons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe("Action Buttons", () => {
it("Clicking 'Set Priority' button shows popconfirm with input and toast on success", () => {
const priority = "99";
cy.dataCy("prioritize-patch").click();
cy.dataCy("patch-priority-input").type(priority).type("{enter}");
cy.dataCy("patch-priority-input").type(`${priority}{enter}`);
cy.validateToast("success", priority);
});

Expand Down
24 changes: 19 additions & 5 deletions cypress/integration/version/name_change_modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ describe("Name change modal", () => {
cy.contains(originalName);
cy.dataCy("name-change-modal-trigger").click();
const newName = "a different name";
cy.get("textarea").clear().type(newName);
cy.get("textarea").clear();
cy.get("textarea").type(newName);
cy.contains("Confirm").click();
cy.get("textarea").should("not.exist");
cy.contains(newName);
cy.validateToast("success", "Patch name was successfully updated.", true);
// revert name change
cy.dataCy("name-change-modal-trigger").click();
cy.get("textarea").clear().type(originalName);
cy.get("textarea").clear();
cy.get("textarea").type(originalName);
cy.contains("Confirm").click();
cy.get("textarea").should("not.exist");
cy.validateToast("success", "Patch name was successfully updated.", true);
Expand All @@ -25,13 +27,25 @@ describe("Name change modal", () => {
it("The confirm button is disabled when the text area value is empty or greater than 300 characters", () => {
cy.dataCy("name-change-modal-trigger").click();
cy.get("textarea").clear();
cy.contains("button", "Confirm").should("be.disabled");
cy.contains("button", "Confirm").should(
"have.attr",
"aria-disabled",
"true"
);
cy.get("textarea").type("lol");
cy.contains("button", "Confirm").should("not.be.disabled");
cy.contains("button", "Confirm").should(
"have.attr",
"aria-disabled",
"false"
);
const over300Chars =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
cy.get("textarea").type(over300Chars);
cy.contains("button", "Confirm").should("be.disabled");
cy.contains("button", "Confirm").should(
"have.attr",
"aria-disabled",
"true"
);
cy.contains("Value cannot exceed 300 characters");
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { hasOperationName, GQL_URL } from "../../../utils/graphql-test-utils";
import { GQL_URL } from "../../../constants";
import { hasOperationName } from "../../../utils/graphql-test-utils";
import { mockErrorResponse } from "../../../utils/mockErrorResponse";

describe("Configure Patch Page", () => {
Expand Down
17 changes: 15 additions & 2 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { EVG_BASE_URL, GQL_URL } from "../constants";
import { hasOperationName } from "../utils/graphql-test-utils";

const user = {
username: "admin",
password: "password",
Expand Down Expand Up @@ -61,14 +64,14 @@ Cypress.Commands.add("getInputByLabel", (label: string) => {
Cypress.Commands.add("login", () => {
cy.getCookie("mci-token").then((c) => {
if (!c) {
cy.request("POST", "http://localhost:9090/login", { ...user });
cy.request("POST", `${EVG_BASE_URL}/login`, { ...user });
}
});
});

/* logout */
Cypress.Commands.add("logout", () => {
cy.origin("http://localhost:9090", () => {
cy.origin(EVG_BASE_URL, () => {
cy.request({ url: "/logout", followRedirect: false });
});
});
Expand Down Expand Up @@ -113,3 +116,13 @@ Cypress.Commands.add(
});
}
);

Cypress.Commands.add("overwriteGQL", (operationName: string, body: any) => {
cy.intercept("POST", GQL_URL, (req) => {
if (hasOperationName(req, operationName)) {
req.reply((res) => {
res.body = body;
});
}
});
});
6 changes: 6 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ declare global {
message?: string,
shouldClose?: boolean
): void;
/**
* Custom command to overwrite a GQL response
* @param operationName - The operation name of the query
* @param body - The replacement response body
*/
overwriteGQL(operationName: string, body: any);
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions cypress/utils/graphql-test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,3 @@ export const aliasMutation = (
req.alias = `gql${operationName}Mutation`;
}
};

export const GQL_URL = "http://localhost:9090/graphql/query";
3 changes: 2 additions & 1 deletion cypress/utils/mockErrorResponse.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GQL_URL, hasOperationName } from "./graphql-test-utils";
import { hasOperationName } from "./graphql-test-utils";
import { GQL_URL } from "../constants";

interface Args {
errorMessage: string;
Expand Down
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ module.exports = {
"yarn prettier --parser graphql",
], // For GraphQL files, run eslint and prettier
"*.{ts,tsx}": () => "tsc -p tsconfig.json --noEmit", // For TypeScript files, run tsc
"*": () => "yarn diff-schema",
"*": () => "yarn check-schema-and-codegen",
};
Loading

0 comments on commit 5c5fe81

Please sign in to comment.