From 8ef01f70ce3c9660fbb9394f3571d3abd04d1f46 Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Wed, 7 Apr 2021 00:06:10 -0700 Subject: [PATCH 01/10] created profile and predictive search by filter tests --- Frontend/cypress/integration/orgchart.spec.js | 0 .../predictive_search_by_filter.spec.js | 296 ++++++++++++++++++ Frontend/cypress/integration/profile.spec.js | 213 +++++++++++++ .../src/components/OrgChartPage/OrgChart.jsx | 1 + .../src/components/ProfilePageContainer.jsx | 1 + .../components/profilePage/CoreInfoArea.jsx | 1 + .../src/components/profilePage/SkillsArea.jsx | 5 +- .../searchArea/ApplyFilterWidget.jsx | 6 +- 8 files changed, 519 insertions(+), 4 deletions(-) create mode 100644 Frontend/cypress/integration/orgchart.spec.js create mode 100644 Frontend/cypress/integration/predictive_search_by_filter.spec.js create mode 100644 Frontend/cypress/integration/profile.spec.js diff --git a/Frontend/cypress/integration/orgchart.spec.js b/Frontend/cypress/integration/orgchart.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/Frontend/cypress/integration/predictive_search_by_filter.spec.js b/Frontend/cypress/integration/predictive_search_by_filter.spec.js new file mode 100644 index 00000000..53127502 --- /dev/null +++ b/Frontend/cypress/integration/predictive_search_by_filter.spec.js @@ -0,0 +1,296 @@ +/// + +describe("Predictive search by filters", () => { + const baseUrl = Cypress.env("baseUrl"); + const timeout = Cypress.env("timeoutInMs"); + + it("non-skill filter", () => { + cy.visit(baseUrl); + + cy.get('[data-cy="loading-filters"]'); + cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); + + // dependent on database knowledge + const testGroups = [ + { + type: "location", + initialLength: 4, + actions: [ + { typeIn: "V", results: ["Vancouver", "Victoria"] }, + { typeIn: "an", results: ["Vancouver"] }, + { typeIn: "xxx", results: [] }, + ], + }, + { + type: "title", + initialLength: 14, + actions: [ + { + typeIn: "CO", + results: [ + "COO", + "Manager-HR/Accounting", + "President and CEO", + "Office Manager - Kelowna", + "Office Manager - Prince George", + ], + }, + { + typeIn: "G", + results: [ + "Manager-HR/Accounting", + "Office Manager - Prince George", + ], + }, + { + typeIn: "E", + results: ["Office Manager - Prince George"], + }, + { typeIn: "A", results: [] }, + ], + }, + { + type: "company", + initialLength: 3, + actions: [ + { + typeIn: "aa", + results: ["Acme Harvesting Ltd.", "Acme Planting Ltd."], + }, + { typeIn: "E", results: ["Acme Harvesting Ltd."] }, + { typeIn: "A", results: [] }, + ], + }, + { + type: "department", + initialLength: 9, + actions: [ + { + typeIn: "at", + results: [ + "Administration", + "Operations", + "Marketing", + "Marketing & Sales", + "Accounting", + ], + }, + { typeIn: "o", results: ["Administration", "Operations"] }, + { typeIn: "S", results: ["Operations"] }, + { typeIn: "d", results: [] }, + ], + }, + ]; + + for (const { type, initialLength, actions } of testGroups) { + cy.get(`[data-cy="expand-${type}-filters"]`).click(); + cy.get(".filter-list-button").should("have.length", initialLength); + cy.get(`[data-cy="expand-${type}-filters"]`).click(); + + for (const action of actions) { + cy.get(`[data-cy="${type}-input"]`).type(action.typeIn); + + for (const result of action.results) { + cy.get(".filter-list-button") + .contains(result) + .should("exist"); + } + + cy.get(".filter-list-button").should( + "have.length", + action.results.length + ); + } + + // clear would auto-hide + cy.get(`[data-cy="${type}-input"]`).clear(); + cy.get(".filter-list-button").should("not.exist"); + } + }); + + it("skill filter", () => { + cy.visit(baseUrl); + + cy.get('[data-cy="loading-filters"]'); + cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); + + const skillArea = cy.get(".filter-form").contains("Filter by skill"); + + // dependent on database knowledge + const getCategoryTitle = (category) => { + return skillArea.get(`[data-cy="category-title-${category}"]`); + }; + + const getCategoryCheckboxes = (category) => { + return skillArea.get(`[data-cy="category-checkboxes-${category}"]`); + }; + + const checkCategoryHasSkills = (cyCategory, array) => { + if (array.length === 0) { + cyCategory.should("not.exist"); + } else { + cyCategory.should("have.length", array.length); + for (const text of array) { + cyCategory.should("contain", text); + } + } + }; + + // initial check + skillArea.get(`[data-cy="expand-skill-filters"]`).click(); + skillArea.get(".category").should("have.length", 4); + + getCategoryCheckboxes("Accounting").should("not.exist"); + getCategoryTitle("Accounting").click(); + checkCategoryHasSkills( + getCategoryCheckboxes("Accounting").find(".filter-list-button"), + ["Auditing", "Reconciling", "Transaction Processing"] + ); + + getCategoryCheckboxes("Agriculture").should("not.exist"); + getCategoryTitle("Agriculture").click(); + checkCategoryHasSkills( + getCategoryCheckboxes("Agriculture").find(".filter-list-button"), + [ + "Fertilizing", + "Harvesting", + "Irrigating", + "Planting", + "Soil Preparation", + ] + ); + + getCategoryCheckboxes("Management").should("not.exist"); + getCategoryTitle("Management").click(); + checkCategoryHasSkills( + getCategoryCheckboxes("Management").find(".filter-list-button"), + ["Budgeting", "Performance Reviews", "Planning"] + ); + + getCategoryCheckboxes("Marketing & Sales").should("not.exist"); + getCategoryTitle("Marketing & Sales").click(); + checkCategoryHasSkills( + getCategoryCheckboxes("Marketing & Sales").find( + ".filter-list-button" + ), + [ + "Customer Service", + "Marketing Strategies", + "Preparing Marketing Materials", + ] + ); + + // on click all hidden + skillArea.get(`[data-cy="expand-skill-filters"]`).click(); + skillArea.get(".category").should("not.exist"); + getCategoryCheckboxes("Accounting").should("not.exist"); + getCategoryCheckboxes("Agriculture").should("not.exist"); + getCategoryCheckboxes("Management").should("not.exist"); + getCategoryCheckboxes("Marketing & Sales").should("not.exist"); + + const actions = [ + { + typeIn: "ac", + results: { + Accounting: [ + "Auditing", + "Reconciling", + "Transaction Processing", + ], + Agriculture: [ + "Fertilizing", + "Harvesting", + "Irrigating", + "Planting", + "Soil Preparation", + ], + Management: ["Performance Reviews"], + "Marketing & Sales": [], + }, + }, + { + typeIn: "c", + results: { + Accounting: [ + "Auditing", + "Reconciling", + "Transaction Processing", + ], + Agriculture: [], + Management: [], + "Marketing & Sales": [], + }, + }, + { + typeIn: "c", + results: { + Accounting: [], + Agriculture: [], + Management: [], + "Marketing & Sales": [], + }, + }, + { + typeIn: "{backspace}{backspace}{backspace}", + results: { + Accounting: [ + "Auditing", + "Reconciling", + "Transaction Processing", + ], + Agriculture: [ + "Fertilizing", + "Harvesting", + "Irrigating", + "Planting", + "Soil Preparation", + ], + Management: [ + "Budgeting", + "Performance Reviews", + "Planning", + ], + "Marketing & Sales": [ + "Customer Service", + "Marketing Strategies", + "Preparing Marketing Materials", + ], + }, + }, + { + typeIn: "{backspace}{backspace}{backspace}", + results: { + Accounting: [], + Agriculture: [], + Management: [], + "Marketing & Sales": [], + }, + }, + ]; + + const categories = [ + "Accounting", + "Agriculture", + "Management", + "Marketing & Sales", + ]; + + for (const { typeIn, results } of actions) { + cy.get(`[data-cy="skill-input"]`).type(typeIn); + for (const category of categories) { + if (results[category].length === 0) { + getCategoryTitle(category).should("not.exist"); + getCategoryCheckboxes(category).should("not.exist"); + } else { + getCategoryTitle(category).should("exist"); + checkCategoryHasSkills( + getCategoryCheckboxes(category).find( + ".filter-list-button" + ), + results[category] + ); + } + } + } + }); +}); diff --git a/Frontend/cypress/integration/profile.spec.js b/Frontend/cypress/integration/profile.spec.js new file mode 100644 index 00000000..76f40c06 --- /dev/null +++ b/Frontend/cypress/integration/profile.spec.js @@ -0,0 +1,213 @@ +/// + +describe("Predictive search by filters", () => { + const baseUrl = Cypress.env("baseUrl"); + const timeout = Cypress.env("timeoutInMs"); + + const coreInfoKeys = [ + "cell", + "phone", + "employeementType", + "yearsPriorExperience", + "division", + "companyName", + "officeLocation", + "physicalLocation", + "hireDate", + ]; + const validWorkers = { + 10003: { + name: "Saul Sampson", + title: "Manager-Marketing", + email: "sampsons@acme.ca", + cell: "604-123-7654", + phone: "604-123-4567", + employeementType: "Salary", + yearsPriorExperience: "0.0", + division: "Marketing", + companyName: "Acme Seeds Inc.", + officeLocation: "Corporate ||| Vancouver", + physicalLocation: "Vancouver", + hireDate: "10/1/1999", + skills: {}, + }, + 60105: { + name: "Allison Martin", + title: "Associate", + email: "martin101@acme.ca", + cell: "778-565-1101", + phone: "604-132-1101", + employeementType: "Salary", + yearsPriorExperience: "2.0", + division: "Accounting", + companyName: "Acme Seeds Inc.", + officeLocation: "Vancouver ||| Corporate", + physicalLocation: "Victoria", + hireDate: "1/1/2020", + skills: { + Agriculture: ["Soil Preparation"], + }, + }, + 60815: { + name: "Amanda Hamiton", + title: "Associate", + email: "hamiton811@acme.ca", + cell: "778-565-1811", + phone: "604-132-1811", + employeementType: "Salary", + yearsPriorExperience: "2.0", + division: "Marketing & Sales", + companyName: "Acme Harvesting Ltd.", + officeLocation: "Kelowna", + physicalLocation: "Victoria", + hireDate: "5/6/1998", + skills: {}, + }, + 10005: { + name: "Connie Conner", + title: "Manager-HR/Accounting", + email: "connerc@acme.ca", + cell: "604-123-7654", + phone: "604-123-4567", + employeementType: "Salary", + yearsPriorExperience: "3.2", + division: "Human Resources", + companyName: "Acme Seeds Inc.", + officeLocation: "Corporate ||| Vancouver", + physicalLocation: "Vancouver", + hireDate: "5/28/1997", + skills: { + Accounting: [ + "Reconciling", + "Transaction Processing", + "Auditing", + ], + Management: ["Planning", "Performance Reviews", "Budgeting"], + Agriculture: ["Harvesting", "Fertilizing", "Soil Preparation"], + "Marketing & Sales": [ + "Preparing Marketing Materials", + "Customer Service", + ], + }, + }, + }; + const checkValidWorker = (workerId) => { + if (!validWorkers[workerId]) { + cy.contains( + "Sorry, there is no employee or contractor with matching id." + ).should("exist"); + } else { + // check card + const card = () => cy.get('[data-cy="employee-card"]'); + const expectedResult = validWorkers[workerId]; + card() + .find(`.card-name-${workerId}`) + .contains(expectedResult.name) + .should("exist"); + card() + .find(`.card-title-${workerId}`) + .contains(expectedResult.title) + .should("exist"); + card() + .find(`.card-email-${workerId}`) + .contains(expectedResult.email) + .should("exist"); + + // check core info + const coreInfo = () => + cy.get('[data-cy="core-info-content"]').find("span"); + for (let counter = 0; counter < coreInfoKeys.length; counter++) { + coreInfo() + .eq(counter) + .contains(expectedResult[coreInfoKeys[counter]]) + .should("exist"); + } + + // check skill + const skills = () => cy.get('[data-cy="profile-skill-content"]'); + const skillRow = (skillCategory) => + skills().find( + `[data-cy="profile-skill-group-${skillCategory}"]` + ); + if (Object.keys(expectedResult.skills).length === 0) { + skills().contains("No skills").should("exist"); + } else { + for (const skillCategory in expectedResult.skills) { + skillRow(skillCategory) + .find("td") + .contains(skillCategory) + .should("exist"); + for (const skill of expectedResult.skills[skillCategory]) { + skillRow(skillCategory) + .find(`[data-cy="profile-skill-chip-${skill}"]`) + .should("exist"); + } + } + } + } + }; + + it("valid workers from url", () => { + cy.visit(`${baseUrl}/profile/10003`); + + cy.get('[data-cy="loading-profile"]'); + cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); + + checkValidWorker(10003); + + cy.visit(`${baseUrl}/profile/10005`); + + cy.get('[data-cy="loading-profile"]'); + cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); + + checkValidWorker(10005); + }); + + + it("invalid worker from url", () => { + cy.visit(`${baseUrl}/profile/123456`); + checkValidWorker(123456); + }); + + it("valid worker from profile + previous / next", () => { + cy.visit(baseUrl); + + cy.get('[data-cy="loading-results"]'); + cy.get('[data-cy="loading-results"]', { timeout }).should("not.exist"); + + cy.contains("Allison Martin").dblclick(); + + cy.get('[data-cy="loading-profile"]').should("not.exist"); + checkValidWorker(60105); + + cy.contains("Next").click(); + + cy.get('[data-cy="loading-profile"]').should("not.exist"); + checkValidWorker(60815); + + cy.contains("Previous").click(); + + cy.get('[data-cy="loading-profile"]').should("not.exist"); + checkValidWorker(60105); + }); + + it("valid worker from org chart", () => { + cy.visit(`${baseUrl}/orgchart/10003`); + + cy.get('[data-cy="loading-orgchart"]'); + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + cy.contains("Profile View").click(); + + cy.get('[data-cy="loading-profile"]').should("not.exist"); + checkValidWorker(10003); + + cy.contains("Organization Chart").click(); + + cy.get('[data-cy="loading-orgchart"]').should("not.exist"); + + cy.contains("Connie Conner").click(); + + checkValidWorker(10005); + }); +}); diff --git a/Frontend/src/components/OrgChartPage/OrgChart.jsx b/Frontend/src/components/OrgChartPage/OrgChart.jsx index bb057a3a..820727d0 100644 --- a/Frontend/src/components/OrgChartPage/OrgChart.jsx +++ b/Frontend/src/components/OrgChartPage/OrgChart.jsx @@ -138,6 +138,7 @@ function OrgChartSearchBar(props) { ) : ( diff --git a/Frontend/src/components/ProfilePageContainer.jsx b/Frontend/src/components/ProfilePageContainer.jsx index 1e12c111..9b9c2cf8 100644 --- a/Frontend/src/components/ProfilePageContainer.jsx +++ b/Frontend/src/components/ProfilePageContainer.jsx @@ -69,6 +69,7 @@ export function ProfilePageContainer(props) { ); diff --git a/Frontend/src/components/profilePage/CoreInfoArea.jsx b/Frontend/src/components/profilePage/CoreInfoArea.jsx index a038dbb6..3f342c38 100644 --- a/Frontend/src/components/profilePage/CoreInfoArea.jsx +++ b/Frontend/src/components/profilePage/CoreInfoArea.jsx @@ -39,6 +39,7 @@ function CoreInfoArea(props) { color="textPrimary" // @ts-ignore component="p" + data-cy="core-info-content" > {information.map((entry) => getInfoEntry(entry[0], entry[1]))} diff --git a/Frontend/src/components/profilePage/SkillsArea.jsx b/Frontend/src/components/profilePage/SkillsArea.jsx index 998d4f53..9b7c0189 100644 --- a/Frontend/src/components/profilePage/SkillsArea.jsx +++ b/Frontend/src/components/profilePage/SkillsArea.jsx @@ -70,7 +70,7 @@ const parseSkillsToTable = ( for (const skillCategory in skillObject) { let skillCounter = 0; skillEntries.push( - + { const skills = {}; skills[skillCategory] = [skill]; @@ -134,7 +135,7 @@ function SkillsArea(props) { Search with these skills - + {parseSkillsToTable( skillObject, styles, diff --git a/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx b/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx index cc96f099..4d6f2dc4 100644 --- a/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx +++ b/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx @@ -153,15 +153,16 @@ function CollapsableCategoryBox(props) { button className="category" onClick={handleExpandMoreClick} + data-cy={`category-title-${label}`} > - + {!expanded ? ( ) : ( )} - + {children} @@ -195,6 +196,7 @@ function CategorizedCheckboxList(props) { filters={filters} appliedFilters={appliedFilters[`${category}`] || []} handleCheckboxChange={handleCheckboxChange} + /> ) From 9e7930955c9e878d54faac4d03dc5d9130ca86ce Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Sat, 10 Apr 2021 21:28:19 -0700 Subject: [PATCH 02/10] finish addinfg tests --- Frontend/cypress/integration/orgchart.spec.js | 136 ++++++++++++++++++ .../predictive_search_by_filter.spec.js | 8 +- Frontend/cypress/integration/profile.spec.js | 77 +++++++--- .../searchArea/ApplyFilterWidget.jsx | 10 +- 4 files changed, 206 insertions(+), 25 deletions(-) diff --git a/Frontend/cypress/integration/orgchart.spec.js b/Frontend/cypress/integration/orgchart.spec.js index e69de29b..31100ab6 100644 --- a/Frontend/cypress/integration/orgchart.spec.js +++ b/Frontend/cypress/integration/orgchart.spec.js @@ -0,0 +1,136 @@ +/// + +describe("Org chart", () => { + const baseUrl = Cypress.env("baseUrl"); + const timeout = Cypress.env("timeoutInMs"); + + const hierachy = { + 10001: { + 10001: [10002, 10101], + }, + 20004: { + 20006: [20002, 20003, { 20004: [20104] }, 20005, 20106], + }, + 20104: { + 20004: [20104], + }, + }; + + // check whether worker is valid, and optionally check for skills to search transition + const checkValidWorker = (workerId) => { + if (!hierachy[workerId]) { + cy.contains( + "Sorry, there is no employee or contractor with matching id." + ).should("exist"); + } else { + const currentHierachy = hierachy[workerId]; + const firstLevelId = Object.keys(currentHierachy)[0]; + const firstLevelDiv = () => cy.get(`#${firstLevelId}`); + firstLevelDiv().should("exist"); + + const secondLevel = currentHierachy[firstLevelId]; + for (const peer of secondLevel) { + if (typeof peer === "number") { + // no subordinate + firstLevelDiv().next().find(`#${peer}`).should("exist"); + } else { + // have subordinates + const centerId = Object.keys(peer)[0]; + const centerDiv = () => + firstLevelDiv().next().find(`#${centerId}`); + + centerDiv().should("exist"); + + const thirdLevelIds = peer[centerId]; + for (const subordinateId of thirdLevelIds) { + centerDiv() + .next() + .find(`#${subordinateId}`) + .should("exist"); + } + } + } + } + }; + + it("valid worker from url", () => { + // no supervisor + cy.visit(`${baseUrl}/orgchart/10001`); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(10001); + + // normal worker + cy.visit(`${baseUrl}/orgchart/20004`); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(20004); + + // no subordinates + cy.visit(`${baseUrl}/orgchart/20104`); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(20104); + }); + + it("valid worker from other tabs", () => { + // from search results (using COO as the tag) + + cy.visit(baseUrl); + + cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); + + cy.get(".MuiChip-deleteIcon", { timeout }).click(); + + cy.get(".filter-form") + .contains("Filter by title") + .get(`[data-cy="expand-title-filters"]`) + .click(); + cy.get(".filter-list-button").contains("President and CEO").click(); + + cy.get('[data-cy="loading-results"]', { timeout }).should("not.exist"); + + cy.get('[data-cy="employee-card"]') + .find('[data-cy="orgchart-icon-10001"]') + .click(); + + checkValidWorker(10001); + + // from profile + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + cy.visit(`${baseUrl}/profile/20104`); + + cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); + + cy.contains("Organization Chart").click(); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(20104); + }); + + it("org chart navigation", () => { + cy.visit(`${baseUrl}/orgchart/20004`); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + cy.get("#20104").click(); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(20104); + }); + + it("invalid worker", () => { + cy.visit(`${baseUrl}/orgchart/100030`); + + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + checkValidWorker(100030); + }); +}); diff --git a/Frontend/cypress/integration/predictive_search_by_filter.spec.js b/Frontend/cypress/integration/predictive_search_by_filter.spec.js index 53127502..8e5d6567 100644 --- a/Frontend/cypress/integration/predictive_search_by_filter.spec.js +++ b/Frontend/cypress/integration/predictive_search_by_filter.spec.js @@ -7,7 +7,7 @@ describe("Predictive search by filters", () => { it("non-skill filter", () => { cy.visit(baseUrl); - cy.get('[data-cy="loading-filters"]'); + cy.get('[data-cy="loading-filters"]').should("exist"); cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); // dependent on database knowledge @@ -23,7 +23,7 @@ describe("Predictive search by filters", () => { }, { type: "title", - initialLength: 14, + initialLength: 15, actions: [ { typeIn: "CO", @@ -103,7 +103,7 @@ describe("Predictive search by filters", () => { } // clear would auto-hide - cy.get(`[data-cy="${type}-input"]`).clear(); + cy.get(`[data-cy="${type}-input"]`).find("input").clear(); cy.get(".filter-list-button").should("not.exist"); } }); @@ -111,7 +111,7 @@ describe("Predictive search by filters", () => { it("skill filter", () => { cy.visit(baseUrl); - cy.get('[data-cy="loading-filters"]'); + cy.get('[data-cy="loading-filters"]').should("exist"); cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); const skillArea = cy.get(".filter-form").contains("Filter by skill"); diff --git a/Frontend/cypress/integration/profile.spec.js b/Frontend/cypress/integration/profile.spec.js index 76f40c06..c6961d35 100644 --- a/Frontend/cypress/integration/profile.spec.js +++ b/Frontend/cypress/integration/profile.spec.js @@ -26,7 +26,7 @@ describe("Predictive search by filters", () => { yearsPriorExperience: "0.0", division: "Marketing", companyName: "Acme Seeds Inc.", - officeLocation: "Corporate ||| Vancouver", + officeLocation: "Corporate, Vancouver", physicalLocation: "Vancouver", hireDate: "10/1/1999", skills: {}, @@ -41,7 +41,7 @@ describe("Predictive search by filters", () => { yearsPriorExperience: "2.0", division: "Accounting", companyName: "Acme Seeds Inc.", - officeLocation: "Vancouver ||| Corporate", + officeLocation: "Corporate, Vancouver", physicalLocation: "Victoria", hireDate: "1/1/2020", skills: { @@ -73,7 +73,7 @@ describe("Predictive search by filters", () => { yearsPriorExperience: "3.2", division: "Human Resources", companyName: "Acme Seeds Inc.", - officeLocation: "Corporate ||| Vancouver", + officeLocation: "Corporate, Vancouver", physicalLocation: "Vancouver", hireDate: "5/28/1997", skills: { @@ -91,7 +91,9 @@ describe("Predictive search by filters", () => { }, }, }; - const checkValidWorker = (workerId) => { + + // check whether worker is valid, and optionally check for skills to search transition + const checkValidWorker = (workerId, checkForSkills = false) => { if (!validWorkers[workerId]) { cy.contains( "Sorry, there is no employee or contractor with matching id." @@ -123,7 +125,7 @@ describe("Predictive search by filters", () => { .should("exist"); } - // check skill + // check skills const skills = () => cy.get('[data-cy="profile-skill-content"]'); const skillRow = (skillCategory) => skills().find( @@ -138,10 +140,43 @@ describe("Predictive search by filters", () => { .contains(skillCategory) .should("exist"); for (const skill of expectedResult.skills[skillCategory]) { - skillRow(skillCategory) - .find(`[data-cy="profile-skill-chip-${skill}"]`) - .should("exist"); + if (!checkForSkills) { + skillRow(skillCategory) + .find(`[data-cy="profile-skill-chip-${skill}"]`) + .should("exist"); + } else { + // click on each chip and make sure search page has the corresponding chip in filter area + skillRow(skillCategory) + .find(`[data-cy="profile-skill-chip-${skill}"]`) + .click(); + + cy.get(".MuiChip-label").should("have.length", 1); + cy.get(".MuiChip-label") + .contains(`${skill} (${skillCategory})`) + .should("exist"); + cy.contains("Profile View").click(); + } + } + } + + // check "search with these skills" + if (checkForSkills) { + cy.contains("Search with these skills").click(); + let totalSkillCount = 0; + for (const skillCategory in expectedResult.skills) { + for (const skill of expectedResult.skills[ + skillCategory + ]) { + cy.get(".MuiChip-label") + .contains(`${skill} (${skillCategory})`) + .should("exist"); + totalSkillCount++; + } } + cy.get(".MuiChip-label").should( + "have.length", + totalSkillCount + ); } } } @@ -150,32 +185,35 @@ describe("Predictive search by filters", () => { it("valid workers from url", () => { cy.visit(`${baseUrl}/profile/10003`); - cy.get('[data-cy="loading-profile"]'); + cy.get('[data-cy="loading-profile"]').should("exist"); cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); - checkValidWorker(10003); + checkValidWorker(10003, true); cy.visit(`${baseUrl}/profile/10005`); - cy.get('[data-cy="loading-profile"]'); + cy.get('[data-cy="loading-profile"]').should("exist"); cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); - checkValidWorker(10005); + checkValidWorker(10005, true); }); - it("invalid worker from url", () => { cy.visit(`${baseUrl}/profile/123456`); + + cy.get('[data-cy="loading-profile"]').should("exist"); + cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); + checkValidWorker(123456); }); it("valid worker from profile + previous / next", () => { cy.visit(baseUrl); - cy.get('[data-cy="loading-results"]'); + cy.get('[data-cy="loading-results"]').should("exist"); cy.get('[data-cy="loading-results"]', { timeout }).should("not.exist"); - cy.contains("Allison Martin").dblclick(); + cy.contains("Allison Martin").click(); cy.get('[data-cy="loading-profile"]').should("not.exist"); checkValidWorker(60105); @@ -194,7 +232,7 @@ describe("Predictive search by filters", () => { it("valid worker from org chart", () => { cy.visit(`${baseUrl}/orgchart/10003`); - cy.get('[data-cy="loading-orgchart"]'); + cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); cy.contains("Profile View").click(); @@ -204,10 +242,13 @@ describe("Predictive search by filters", () => { cy.contains("Organization Chart").click(); - cy.get('[data-cy="loading-orgchart"]').should("not.exist"); - cy.contains("Connie Conner").click(); + cy.get('[data-cy="loading-orgchart"]').should("exist"); + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + + cy.contains("Profile View").click(); + checkValidWorker(10005); }); }); diff --git a/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx b/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx index 4d6f2dc4..55175d15 100644 --- a/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx +++ b/Frontend/src/components/searchPage/searchArea/ApplyFilterWidget.jsx @@ -155,14 +155,19 @@ function CollapsableCategoryBox(props) { onClick={handleExpandMoreClick} data-cy={`category-title-${label}`} > - + {!expanded ? ( ) : ( )} - + {children} @@ -196,7 +201,6 @@ function CategorizedCheckboxList(props) { filters={filters} appliedFilters={appliedFilters[`${category}`] || []} handleCheckboxChange={handleCheckboxChange} - /> ) From 3091a7e55e44ee5c93326d914e7429fb10cfeaca Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Sat, 10 Apr 2021 22:20:10 -0700 Subject: [PATCH 03/10] update timeout handling --- Frontend/cypress/integration/orgchart.spec.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Frontend/cypress/integration/orgchart.spec.js b/Frontend/cypress/integration/orgchart.spec.js index 31100ab6..19346357 100644 --- a/Frontend/cypress/integration/orgchart.spec.js +++ b/Frontend/cypress/integration/orgchart.spec.js @@ -57,6 +57,7 @@ describe("Org chart", () => { // no supervisor cy.visit(`${baseUrl}/orgchart/10001`); + cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); checkValidWorker(10001); @@ -64,6 +65,7 @@ describe("Org chart", () => { // normal worker cy.visit(`${baseUrl}/orgchart/20004`); + cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); checkValidWorker(20004); @@ -71,6 +73,7 @@ describe("Org chart", () => { // no subordinates cy.visit(`${baseUrl}/orgchart/20104`); + cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); checkValidWorker(20104); @@ -81,6 +84,7 @@ describe("Org chart", () => { cy.visit(baseUrl); + cy.get('[data-cy="loading-filters"]').should("exist"); cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); cy.get(".MuiChip-deleteIcon", { timeout }).click(); @@ -91,25 +95,27 @@ describe("Org chart", () => { .click(); cy.get(".filter-list-button").contains("President and CEO").click(); + cy.get('[data-cy="loading-results"]').should("exist"); cy.get('[data-cy="loading-results"]', { timeout }).should("not.exist"); cy.get('[data-cy="employee-card"]') .find('[data-cy="orgchart-icon-10001"]') .click(); + cy.get('[data-cy="loading-orgchart"]').should("exist"); + cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + checkValidWorker(10001); // from profile - cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); - cy.visit(`${baseUrl}/profile/20104`); - cy.get('[data-cy="loading-profile"]', { timeout }).should("not.exist"); + cy.get('[data-cy="loading-profile"]').should("not.exist"); cy.contains("Organization Chart").click(); - cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); + cy.get('[data-cy="loading-orgchart"]').should("not.exist"); checkValidWorker(20104); }); @@ -129,6 +135,7 @@ describe("Org chart", () => { it("invalid worker", () => { cy.visit(`${baseUrl}/orgchart/100030`); + cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); checkValidWorker(100030); From 030305acc6154f47218cac1b21c6abb9d81ffeb5 Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Sat, 10 Apr 2021 23:11:52 -0700 Subject: [PATCH 04/10] update count --- .../cypress/integration/predictive_search_by_filter.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/cypress/integration/predictive_search_by_filter.spec.js b/Frontend/cypress/integration/predictive_search_by_filter.spec.js index 8e5d6567..5c3e6480 100644 --- a/Frontend/cypress/integration/predictive_search_by_filter.spec.js +++ b/Frontend/cypress/integration/predictive_search_by_filter.spec.js @@ -23,7 +23,7 @@ describe("Predictive search by filters", () => { }, { type: "title", - initialLength: 15, + initialLength: 14, actions: [ { typeIn: "CO", From f2be7b2e1eebd0497edd363f31bb8c89166195bd Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Mon, 12 Apr 2021 00:56:12 -0700 Subject: [PATCH 05/10] remove checks for title filter lengths --- .../predictive_search_by_filter.spec.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Frontend/cypress/integration/predictive_search_by_filter.spec.js b/Frontend/cypress/integration/predictive_search_by_filter.spec.js index 5c3e6480..7bc2a8eb 100644 --- a/Frontend/cypress/integration/predictive_search_by_filter.spec.js +++ b/Frontend/cypress/integration/predictive_search_by_filter.spec.js @@ -23,7 +23,6 @@ describe("Predictive search by filters", () => { }, { type: "title", - initialLength: 14, actions: [ { typeIn: "CO", @@ -84,7 +83,10 @@ describe("Predictive search by filters", () => { for (const { type, initialLength, actions } of testGroups) { cy.get(`[data-cy="expand-${type}-filters"]`).click(); - cy.get(".filter-list-button").should("have.length", initialLength); + // do not check initial length of title filters (db could add random titles) + if (type !== "title") { + cy.get(".filter-list-button").should("have.length", initialLength); + } cy.get(`[data-cy="expand-${type}-filters"]`).click(); for (const action of actions) { @@ -95,11 +97,13 @@ describe("Predictive search by filters", () => { .contains(result) .should("exist"); } - - cy.get(".filter-list-button").should( - "have.length", - action.results.length - ); + // again, do not check length of title filters (db could add random titles) + if (type !== "title") { + cy.get(".filter-list-button").should( + "have.length", + action.results.length + ); + } } // clear would auto-hide From d4ecb242fc3079152c551103cf1c314c54a806cd Mon Sep 17 00:00:00 2001 From: eqfy Date: Tue, 13 Apr 2021 13:27:17 -0700 Subject: [PATCH 06/10] Delete duplicate data-cy=search-by-name --- .../src/components/searchPage/searchArea/SearchByNameBar.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx b/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx index 355b2170..4f2ea797 100644 --- a/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx +++ b/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx @@ -126,7 +126,6 @@ function SearchByNameBar(props) { getOptionLabel={() => inputValue} openOnFocus={true} freeSolo={true} - data-cy="search-by-name" renderInput={(params) => ( Date: Tue, 13 Apr 2021 13:47:31 -0700 Subject: [PATCH 07/10] Fixed loading bug for add contractor supervisor --- Frontend/src/components/AddContractor.jsx | 68 ++++++++++++----------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/Frontend/src/components/AddContractor.jsx b/Frontend/src/components/AddContractor.jsx index b6cdc59f..88ddaa91 100644 --- a/Frontend/src/components/AddContractor.jsx +++ b/Frontend/src/components/AddContractor.jsx @@ -30,7 +30,10 @@ import { PagePathEnum } from "./common/constants"; import { Storage } from "aws-amplify"; import config from "../config"; import { coordinatedDebounce } from "./common/helpers"; -import { setSnackbarState } from 'actions/generalAction'; +import { setSnackbarState } from "actions/generalAction"; + +// counter for timeout in case of supervisor input change +const predictiveSearchTimer = {}; function AddContractor(props) { const { filterData, isAdmin, setSnackbarState } = props; @@ -61,8 +64,6 @@ function AddContractor(props) { selectedSkills: [], }; const [formState, setFormState] = React.useState(defaultState); - // counter for timeout in case of supervisor input change - const predictiveSearchTimer = {}; React.useEffect(() => { if ( @@ -504,10 +505,10 @@ function AddContractor(props) { /> @@ -532,10 +533,12 @@ function AddContractor(props) { /> )} renderOption={(option) => + option === "loading" || formState.loadingState["supervisor"] ? ( -
+ data-cy="loading-supervisor-result" + >

option} className={classes.textField} renderInput={(params) => ( @@ -736,24 +739,23 @@ function AddContractor(props) { /> )} /> -
- +
+
{ }; const mapDispatchToProps = (dispatch) => ({ - setSnackbarState: (snackbarState) => dispatch(setSnackbarState(snackbarState)), + setSnackbarState: (snackbarState) => + dispatch(setSnackbarState(snackbarState)), }); -export default withRouter(connect(mapStateToProps, - mapDispatchToProps)(AddContractor)); +export default withRouter( + connect(mapStateToProps, mapDispatchToProps)(AddContractor) +); const useStyles = makeStyles(() => ({ root: { display: "flex", - justifyContent:"center", + justifyContent: "center", }, input: { display: "none", @@ -813,7 +817,7 @@ const useStyles = makeStyles(() => ({ }, submitBtnWrapper: { display: "flex", - justifyContent:"center", + justifyContent: "center", }, submitBtn: { width: 200, From 638b6e4182d0913735b482d8c3297f98bf92fa49 Mon Sep 17 00:00:00 2001 From: eqfy Date: Tue, 13 Apr 2021 14:13:59 -0700 Subject: [PATCH 08/10] Fix failing E2E tests --- Frontend/cypress/integration/orgchart.spec.js | 9 +++---- .../predictive_search_by_filter.spec.js | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Frontend/cypress/integration/orgchart.spec.js b/Frontend/cypress/integration/orgchart.spec.js index 19346357..f1e998c0 100644 --- a/Frontend/cypress/integration/orgchart.spec.js +++ b/Frontend/cypress/integration/orgchart.spec.js @@ -91,20 +91,17 @@ describe("Org chart", () => { cy.get(".filter-form") .contains("Filter by title") - .get(`[data-cy="expand-title-filters"]`) + .get(`[data-cy="expand-title-filters"]`, { timeout }) .click(); cy.get(".filter-list-button").contains("President and CEO").click(); - cy.get('[data-cy="loading-results"]').should("exist"); - cy.get('[data-cy="loading-results"]', { timeout }).should("not.exist"); - - cy.get('[data-cy="employee-card"]') + cy.get('[data-cy="employee-card"]', { timeout }) .find('[data-cy="orgchart-icon-10001"]') .click(); cy.get('[data-cy="loading-orgchart"]').should("exist"); cy.get('[data-cy="loading-orgchart"]', { timeout }).should("not.exist"); - + checkValidWorker(10001); // from profile diff --git a/Frontend/cypress/integration/predictive_search_by_filter.spec.js b/Frontend/cypress/integration/predictive_search_by_filter.spec.js index 7bc2a8eb..3ca07850 100644 --- a/Frontend/cypress/integration/predictive_search_by_filter.spec.js +++ b/Frontend/cypress/integration/predictive_search_by_filter.spec.js @@ -85,7 +85,10 @@ describe("Predictive search by filters", () => { cy.get(`[data-cy="expand-${type}-filters"]`).click(); // do not check initial length of title filters (db could add random titles) if (type !== "title") { - cy.get(".filter-list-button").should("have.length", initialLength); + cy.get(".filter-list-button").should( + "have.length", + initialLength + ); } cy.get(`[data-cy="expand-${type}-filters"]`).click(); @@ -102,7 +105,7 @@ describe("Predictive search by filters", () => { cy.get(".filter-list-button").should( "have.length", action.results.length - ); + ); } } @@ -118,15 +121,18 @@ describe("Predictive search by filters", () => { cy.get('[data-cy="loading-filters"]').should("exist"); cy.get('[data-cy="loading-filters"]', { timeout }).should("not.exist"); - const skillArea = cy.get(".filter-form").contains("Filter by skill"); + const skillArea = () => + cy.get(".filter-form").contains("Filter by skill"); // dependent on database knowledge const getCategoryTitle = (category) => { - return skillArea.get(`[data-cy="category-title-${category}"]`); + return skillArea().get(`[data-cy="category-title-${category}"]`); }; const getCategoryCheckboxes = (category) => { - return skillArea.get(`[data-cy="category-checkboxes-${category}"]`); + return skillArea().get( + `[data-cy="category-checkboxes-${category}"]` + ); }; const checkCategoryHasSkills = (cyCategory, array) => { @@ -141,8 +147,8 @@ describe("Predictive search by filters", () => { }; // initial check - skillArea.get(`[data-cy="expand-skill-filters"]`).click(); - skillArea.get(".category").should("have.length", 4); + skillArea().get(`[data-cy="expand-skill-filters"]`).click(); + skillArea().get(".category").should("have.length", 4); getCategoryCheckboxes("Accounting").should("not.exist"); getCategoryTitle("Accounting").click(); @@ -185,8 +191,8 @@ describe("Predictive search by filters", () => { ); // on click all hidden - skillArea.get(`[data-cy="expand-skill-filters"]`).click(); - skillArea.get(".category").should("not.exist"); + skillArea().get(`[data-cy="expand-skill-filters"]`).click(); + skillArea().get(".category").should("not.exist"); getCategoryCheckboxes("Accounting").should("not.exist"); getCategoryCheckboxes("Agriculture").should("not.exist"); getCategoryCheckboxes("Management").should("not.exist"); From bbd989d3bc48979f2df66ec5bf55266dd09cb071 Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Tue, 13 Apr 2021 15:20:23 -0700 Subject: [PATCH 09/10] update based on feedback --- Frontend/cypress/integration/orgchart.spec.js | 14 +++++--- Frontend/cypress/integration/profile.spec.js | 34 +++++++++---------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Frontend/cypress/integration/orgchart.spec.js b/Frontend/cypress/integration/orgchart.spec.js index f1e998c0..88f75914 100644 --- a/Frontend/cypress/integration/orgchart.spec.js +++ b/Frontend/cypress/integration/orgchart.spec.js @@ -4,7 +4,7 @@ describe("Org chart", () => { const baseUrl = Cypress.env("baseUrl"); const timeout = Cypress.env("timeoutInMs"); - const hierachy = { + const hierarchy = { 10001: { 10001: [10002, 10101], }, @@ -18,17 +18,20 @@ describe("Org chart", () => { // check whether worker is valid, and optionally check for skills to search transition const checkValidWorker = (workerId) => { - if (!hierachy[workerId]) { + if (!hierarchy[workerId]) { cy.contains( "Sorry, there is no employee or contractor with matching id." ).should("exist"); } else { - const currentHierachy = hierachy[workerId]; - const firstLevelId = Object.keys(currentHierachy)[0]; + // check for current + cy.get(`#${workerId}`).find(".current").should("exist"); + + const currentHierarchy = hierarchy[workerId]; + const firstLevelId = Object.keys(currentHierarchy)[0]; const firstLevelDiv = () => cy.get(`#${firstLevelId}`); firstLevelDiv().should("exist"); - const secondLevel = currentHierachy[firstLevelId]; + const secondLevel = currentHierarchy[firstLevelId]; for (const peer of secondLevel) { if (typeof peer === "number") { // no subordinate @@ -108,6 +111,7 @@ describe("Org chart", () => { cy.visit(`${baseUrl}/profile/20104`); + cy.get('[data-cy="loading-profile"]').should("exist"); cy.get('[data-cy="loading-profile"]').should("not.exist"); cy.contains("Organization Chart").click(); diff --git a/Frontend/cypress/integration/profile.spec.js b/Frontend/cypress/integration/profile.spec.js index c6961d35..8e0ae8eb 100644 --- a/Frontend/cypress/integration/profile.spec.js +++ b/Frontend/cypress/integration/profile.spec.js @@ -104,16 +104,13 @@ describe("Predictive search by filters", () => { const expectedResult = validWorkers[workerId]; card() .find(`.card-name-${workerId}`) - .contains(expectedResult.name) - .should("exist"); + .should("contain.text", expectedResult.name); card() .find(`.card-title-${workerId}`) - .contains(expectedResult.title) - .should("exist"); + .should("contain.text", expectedResult.title); card() .find(`.card-email-${workerId}`) - .contains(expectedResult.email) - .should("exist"); + .should("contain.text", expectedResult.email); // check core info const coreInfo = () => @@ -121,8 +118,10 @@ describe("Predictive search by filters", () => { for (let counter = 0; counter < coreInfoKeys.length; counter++) { coreInfo() .eq(counter) - .contains(expectedResult[coreInfoKeys[counter]]) - .should("exist"); + .should( + "contain.text", + expectedResult[coreInfoKeys[counter]] + ); } // check skills @@ -132,13 +131,12 @@ describe("Predictive search by filters", () => { `[data-cy="profile-skill-group-${skillCategory}"]` ); if (Object.keys(expectedResult.skills).length === 0) { - skills().contains("No skills").should("exist"); + skills().should("contain.text", "No skills"); } else { for (const skillCategory in expectedResult.skills) { skillRow(skillCategory) .find("td") - .contains(skillCategory) - .should("exist"); + .should("contain.text", skillCategory); for (const skill of expectedResult.skills[skillCategory]) { if (!checkForSkills) { skillRow(skillCategory) @@ -151,9 +149,10 @@ describe("Predictive search by filters", () => { .click(); cy.get(".MuiChip-label").should("have.length", 1); - cy.get(".MuiChip-label") - .contains(`${skill} (${skillCategory})`) - .should("exist"); + cy.get(".MuiChip-label").should( + "contain.text", + `${skill} (${skillCategory})` + ); cy.contains("Profile View").click(); } } @@ -167,9 +166,10 @@ describe("Predictive search by filters", () => { for (const skill of expectedResult.skills[ skillCategory ]) { - cy.get(".MuiChip-label") - .contains(`${skill} (${skillCategory})`) - .should("exist"); + cy.get(".MuiChip-label").should( + "contain.text", + `${skill} (${skillCategory})` + ); totalSkillCount++; } } From 8d5a68c1254a87428807c699cba474ee1a6fed5b Mon Sep 17 00:00:00 2001 From: Maxwell-Yang-2001 Date: Tue, 13 Apr 2021 15:23:58 -0700 Subject: [PATCH 10/10] resolve search-by-name duplication --- .../src/components/searchPage/searchArea/SearchByNameBar.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx b/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx index b61a10d7..4f2ea797 100644 --- a/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx +++ b/Frontend/src/components/searchPage/searchArea/SearchByNameBar.jsx @@ -132,6 +132,7 @@ function SearchByNameBar(props) { variant="outlined" label="Search by name" size="small" + data-cy="search-by-name" /> )} renderOption={(option, state) => {