Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fe:FSADT1-1521): create the predictive search #1223

Merged
merged 23 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
24b827f
feat: add search box with predictive search
fterra-encora Oct 7, 2024
80e469c
fix: fix predictive-search stubs
fterra-encora Oct 7, 2024
0126363
feat: update style of search controls
fterra-encora Oct 7, 2024
f5e9c8e
docs: update interface name
fterra-encora Oct 7, 2024
2dd07a2
fix: use placeholder instead of label
fterra-encora Oct 7, 2024
cb092f9
test: add search test file
fterra-encora Oct 7, 2024
4b11f81
Merge branch 'feat/client3' into feat/FSADT1-1521
fterra-encora Oct 7, 2024
7fdffcf
feat: navigate to client details
fterra-encora Oct 8, 2024
a17261e
test: implement tests
fterra-encora Oct 8, 2024
07c3274
refactor: move functions to the GlobalValidators
fterra-encora Oct 8, 2024
1e7dbd2
feat: validate autocomplete while typing
fterra-encora Oct 8, 2024
fb567c9
feat: validate keywords
fterra-encora Oct 8, 2024
4375ad1
test: validate keywords
fterra-encora Oct 8, 2024
ae2676b
Merge branch 'feat/client3' into feat/FSADT1-1521
fterra-encora Oct 8, 2024
97f11f7
Merge branch 'feat/client3' into feat/FSADT1-1521
mamartinezmejia Oct 9, 2024
2aff87f
Merge branch 'feat/client3' into feat/FSADT1-1521
mamartinezmejia Oct 9, 2024
a6a3234
feat: prevent selection on AutoComplete
fterra-encora Oct 9, 2024
a6d44d2
feat: open client details in a new tab
fterra-encora Oct 9, 2024
bece3bd
chore: update endpoint stub
fterra-encora Oct 9, 2024
b082585
test: update tests
fterra-encora Oct 9, 2024
c2c09fe
feat(fe:FSADT1-1541): Display an error message when the BE is down (#…
mamartinezmejia Oct 9, 2024
2a48e02
Merge branch 'feat/client3' into feat/FSADT1-1521
fterra-encora Oct 9, 2024
f46f681
fix: rename event from click to click:option
fterra-encora Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions frontend/cypress/e2e/pages/SearchPage.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import type { ClientSearchResult } from "@/dto/CommonTypesDto";

describe("Search Page", () => {
const predictiveSearchCounter = {
count: 0,
};

const checkDisplayedResults = (clientList: ClientSearchResult[]) => {
clientList.forEach((client) => {
cy.get("#search-box")
.find(`cds-combo-box-item[data-value^="${client.clientNumber}"]`)
.should("exist");
});
};
beforeEach(() => {
// reset counter
predictiveSearchCounter.count = 0;

cy.intercept("/api/clients/search?keyword=*", (req) => {
predictiveSearchCounter.count++;
req.continue();
}).as("predictiveSearch");

cy.viewport(1920, 1080);
cy.visit("/");

cy.login("[email protected]", "Uat Test", "idir", {
given_name: "James",
family_name: "Baxter",
"cognito:groups": ["CLIENT_VIEWER"],
});

// Check if the Client search button is visible
cy.get("#menu-list-search").should("be.visible").click();

cy.get("h1").should("be.visible").should("contain", "Client search");

cy.window().then((win) => {
cy.stub(win, "open").as("windowOpen");
});
});

describe("when user fills in the search box with a valid value", () => {
beforeEach(() => {
cy.fillFormEntry("#search-box", "car", { skipBlur: true });
});

it("makes the API call with the entered keywords", () => {
cy.wait("@predictiveSearch").then((interception) => {
expect(interception.request.query.keyword).to.eq("car");
});
cy.wrap(predictiveSearchCounter).its("count").should("eq", 1);
});

it("displays autocomplete results", () => {
cy.get("#search-box")
.find("cds-combo-box-item")
.should("have.length", 3)
.should("be.visible");

cy.wait("@predictiveSearch").then((interception) => {
const data = interception.response.body;

cy.wrap(data).should("be.an", "array").and("have.length", 3);

cy.get("#search-box")
.find("cds-combo-box-item")
.should("have.length", data.length)
.should("be.visible");

checkDisplayedResults(data);
});
});

describe("and types more characters", () => {
beforeEach(() => {
cy.wait("@predictiveSearch");
cy.fillFormEntry("#search-box", "d", { skipBlur: true });
});

it("makes another the API call with the updated keywords", () => {
cy.wait("@predictiveSearch").then((interception) => {
expect(interception.request.query.keyword).to.eq("card");
});
cy.wrap(predictiveSearchCounter).its("count").should("eq", 2);
});

it("updates the autocomplete results", () => {
cy.wait("@predictiveSearch").then((interception) => {
const data = interception.response.body;

cy.wrap(data).should("be.an", "array").and("have.length.greaterThan", 3);

cy.get("#search-box")
.find("cds-combo-box-item")
.should("have.length", data.length)
.should("be.visible");

checkDisplayedResults(data);
});
});
});

describe("and user clicks a result", () => {
const clientNumber = "00001297";
beforeEach(() => {
cy.get("#search-box")
.find("cds-combo-box-item")
.should("have.length", 3)
.should("be.visible");

cy.get("#search-box").find(`cds-combo-box-item[data-value^="${clientNumber}"]`).click();
});
it("navigates to the client details", () => {
const greenDomain = "green-domain.com";
cy.get("@windowOpen").should(
"be.calledWith",
`https://${greenDomain}/int/client/client02MaintenanceAction.do?bean.clientNumber=${clientNumber}`,
"_blank",
"noopener",
);
});
});
});

describe("when user fills in the search box with an invalid value", () => {
beforeEach(() => {
cy.fillFormEntry("#search-box", "até", { skipBlur: true });
});

it("shows an error message", () => {
cy.contains("The search terms can only contain: A-Z, a-z, 0-9, space or common symbols");
});
it("makes no API call", () => {
cy.wait(500); // This time has to be greater than the debouncing time
cy.wrap(predictiveSearchCounter).its("count").should("eq", 0);
});
});
});
46 changes: 38 additions & 8 deletions frontend/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,45 @@ Cypress.Commands.add("getMany", (names: string[]): Cypress.Chainable<any[]> => {
return cy.wrap(values);
});

Cypress.Commands.add("fillFormEntry",(field: string, value: string, delayMS: number = 10, area: boolean = false) =>{
interface FillFormEntryOptions {
delayMS?: number;
area?: boolean;
skipBlur?: boolean;
}

interface FillFormEntry {
(field: string, value: string, delayMS?: number, area?: boolean): void;
(field: string, value: string, options?: FillFormEntryOptions): void;
}

const fillFormEntry: FillFormEntry = (
field: string,
value: string,
arg3: number | FillFormEntryOptions = 10,
arg4: boolean | never = false,
) => {
const options =
typeof arg3 === "object"
? arg3
: {
delayMS: arg3,
area: arg4,
};
const { delayMS, area, skipBlur } = options;
cy.get(field)
.should("exist")
.shadow()
.find(area ? "textarea" : "input")
.focus()
.type(value,{ delay: delayMS })
.blur();
});
.should("exist")
.shadow()
.find(area ? "textarea" : "input")
.focus()
.type(value, { delay: delayMS })
.then((subject) => {
if (!skipBlur) {
cy.wrap(subject).blur();
}
});
};

Cypress.Commands.add("fillFormEntry", fillFormEntry);

Cypress.Commands.add("clearFormEntry",(field: string, area: boolean = false) =>{
cy.get(field)
Expand Down
15 changes: 13 additions & 2 deletions frontend/cypress/support/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@ declare namespace Cypress {
login(email: string, name: string, provider: string, extras: any = "{}"): Chainable<void>;
logout(): Chainable<void>;
getMany(names: string[]): Chainable<any[]>;
fillFormEntry(field: string, value: string, delayMS: number = 10, area: boolean = false): Chainable<void>;
fillFormEntry(
field: string,
value: string,
delayMS: number = 10,
area: boolean = false,
): Chainable<void>;
fillFormEntry(field: string, value: string, options: FillFormEntryOptions): Chainable<void>;
clearFormEntry(field: string, area: boolean = false): Chainable<void>;
selectFormEntry(field: string, value: string, box: boolean): Chainable<void>;
markCheckbox(field: string): Chainable<void>;
unmarkCheckbox(field: string): Chainable<void>;
selectAutocompleteEntry(field: string, value: string, dataid: string, delayTarget: string =''): Chainable<void>;
selectAutocompleteEntry(
field: string,
value: string,
dataid: string,
delayTarget: string = "",
): Chainable<void>;
checkInputErrorMessage(field: string, message: string): Chainable<void>;
checkAutoCompleteErrorMessage(field: string, message: string): Chainable<void>;
checkAccordionItemState(additionalSelector: string, open: boolean): Chainable<void>;
Expand Down
6 changes: 3 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"scripts": {
"start": "vite --host --port 3000",
"build": "vue-tsc --noEmit && vite build",
"preview": "cross-env VITE_NODE_ENV=test VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test stub http://127.0.0.1:8080 start",
"preview": "cross-env VITE_NODE_ENV=test VITE_GREEN_DOMAIN=green-domain.com VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test stub http://127.0.0.1:8080 start",
"preview:app": "cross-env VITE_NODE_ENV=test vite --host --port 3000",
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --fix --ignore-path .gitignore",
Expand All @@ -36,12 +36,12 @@
"posttest:component": "mv reports/.nyc_report reports/component",
"test:unit": "cross-env VITE_NODE_ENV=test NODE_ENV=test vitest run --coverage",
"posttest:unit": "mv reports/.vite_report reports/unit",
"test:e2e": "cross-env VITE_NODE_ENV=test VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test preview http://127.0.0.1:3000 'cypress run --headless'",
"test:e2e": "cross-env VITE_NODE_ENV=test VITE_GREEN_DOMAIN=green-domain.com VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test preview http://127.0.0.1:3000 'cypress run --headless'",
"posttest:e2e": "mv reports/.nyc_report reports/e2e",
"pretest:report:merge": "rm -rf reports-merge && mkdir -p reports-merge && for name in component e2e unit; do cp reports/$name/coverage-final.json reports-merge/$name.json; done",
"test:report:merge": "mkdir -p .nyc_output && rm -rf coverage && nyc --config nyc.config.json merge reports-merge && mv coverage.json .nyc_output/out.json && nyc --config nyc.config.json report --reporter lcov --reporter text-summary --report-dir coverage --temp-dir .nyc_output",
"test:report:clean": "rm -rf reports && mkdir -p reports/.nyc_output/processinfo && mkdir -p coverage",
"test:build": "cross-env VITE_MODE=test VITE_NODE_ENV=test VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test preview http://127.0.0.1:3000 'cypress open'",
"test:build": "cross-env VITE_MODE=test VITE_NODE_ENV=test VITE_GREEN_DOMAIN=green-domain.com VITE_FEATURE_FLAGS={\\\"STAFF_SEARCH\\\":true} start-server-and-test preview http://127.0.0.1:3000 'cypress open'",
"test:flush": "rm -rf reports && rm -rf .nyc_output && rm -rf coverage && rm -rf reports-merge",
"posttest:flush": "npm run coverage",
"test:unit:devtools": "cross-env VITE_NODE_ENV=test NODE_ENV=test vitest run --inspect-brk --pool threads --poolOptions.threads.singleThread"
Expand Down
34 changes: 19 additions & 15 deletions frontend/src/assets/styles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,25 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01 {
background-color: color-mix(in srgb, var(--cds-support-warning) 40%, white);
}

#datatable {
position: relative;
right: 0;
overflow-x: scroll;
}

#search-line {
display: flex;
align-items: flex-end;

.grouping-02 {
flex-grow: 1;
}
}

#search-button {
width: 7.875rem;
}

/* Small (up to 671px) */
@media screen and (max-width: 671px) {
:root {
Expand Down Expand Up @@ -1807,11 +1826,6 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01 {
.submission-details--title svg {
width: 5rem;
}
#datatable {
position: relative;
right: 0;
overflow-x: scroll;
}
cds-table {
width: 82rem;
}
Expand Down Expand Up @@ -1920,11 +1934,6 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01 {
.paginator {
width: 100vw;
}
#datatable {
position: relative;
right: 0;
overflow-x: scroll;
}
cds-table {
width: 82rem;
}
Expand Down Expand Up @@ -1999,11 +2008,6 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01 {
);
}

#datatable {
position: relative;
right: 0;
overflow-x: scroll;
}
cds-table {
min-width: 66rem;
}
Expand Down
Loading
Loading