diff --git a/core/src/app/(public)/(with-toolbar)/indicator/[id]/page.tsx b/core/src/app/(public)/(with-toolbar)/indicator/[id]/page.tsx index 19a6d7b..d271694 100644 --- a/core/src/app/(public)/(with-toolbar)/indicator/[id]/page.tsx +++ b/core/src/app/(public)/(with-toolbar)/indicator/[id]/page.tsx @@ -73,6 +73,4 @@ async function IndicatorPage({ params }: IndicatorPageProps) { export const revalidate = "force-cache" -export const dynamicParams = process.env.SSG === "false" ? true : false - export default IndicatorPage diff --git a/core/src/containers/forms/value-form/create-value-form/CreateValueForm.tsx b/core/src/containers/forms/value-form/create-value-form/CreateValueForm.tsx index a0e25d9..3bb902d 100644 --- a/core/src/containers/forms/value-form/create-value-form/CreateValueForm.tsx +++ b/core/src/containers/forms/value-form/create-value-form/CreateValueForm.tsx @@ -1,6 +1,7 @@ import { FC } from "react" import { useForm } from "react-hook-form" import { yupResolver } from "@hookform/resolvers/yup" +import { useRouter } from "next/navigation" import Button from "@/ui/button/Button" import Input from "@/ui/input/Input" import Label from "@/ui/label/Label" @@ -16,10 +17,15 @@ import { createValue } from "@/api/admin" import "@/containers/forms/value-form/styles.scss" const CreateValueForm: FC = ({ onSuccess }) => { + const router = useRouter() + const [data, mutate] = useMutation(createValue, { successMessage: "Value was created successffully", errorMessage: "Unexpected error occured", - onSuccess, + onSuccess: () => { + onSuccess() + router.refresh() + }, }) const { diff --git a/core/src/containers/modals/value-modal/ValueModal.tsx b/core/src/containers/modals/value-modal/ValueModal.tsx index 399a6b2..347ad43 100644 --- a/core/src/containers/modals/value-modal/ValueModal.tsx +++ b/core/src/containers/modals/value-modal/ValueModal.tsx @@ -4,14 +4,14 @@ import { ValueModalProps } from "@/containers/modals/value-modal/types" import DataList from "@/components/data-list/DataList" import DataListItem from "@/components/data-list/components/data-list-item/DataListItem" import ModalContainer from "@/components/modal-container/ModalContainer" - -//import { useModal } from "@/providers/modal-provider/ModalProvider" +import { useModal } from "@/providers/modal-provider/ModalProvider" +import EditValueModal from "../edit-value-modal/EditValueModal" const ValueModal: FC = ({ value }) => { - //const { openModal } = useModal() + const { openModal } = useModal() const handleEditValue = () => { - //openModal() + openModal() } const createdAtDate = new Date(value.createdAt).toLocaleDateString() @@ -20,15 +20,48 @@ const ValueModal: FC = ({ value }) => { return ( - - - - - - - + + + + + + + - diff --git a/core/src/containers/values-dashboard-header/ValuesDashboardHeader.tsx b/core/src/containers/values-dashboard-header/ValuesDashboardHeader.tsx index acd843e..5154407 100644 --- a/core/src/containers/values-dashboard-header/ValuesDashboardHeader.tsx +++ b/core/src/containers/values-dashboard-header/ValuesDashboardHeader.tsx @@ -19,7 +19,10 @@ const ValuesDashboardHeader = () => { const handleRefresh = () => router.refresh() return ( -
+
{ - +
diff --git a/core/src/containers/values-dashboard-table/ValuesDashboardTable.tsx b/core/src/containers/values-dashboard-table/ValuesDashboardTable.tsx index 79c212a..0cd854d 100644 --- a/core/src/containers/values-dashboard-table/ValuesDashboardTable.tsx +++ b/core/src/containers/values-dashboard-table/ValuesDashboardTable.tsx @@ -27,7 +27,7 @@ const ValuesDashboardTable: FC = () => { const ref = useInfiniteScroll(getAdminValues, merge, { initialPage: 1 }) return ( -
+
= ({ semantic={false} className="admin-dashboard-table__check-cell flex-5" > - + @@ -79,6 +84,7 @@ const IndicatorsDashboardTableRow: FC = ({ setIsOptionsDropdownOpened(true)} > diff --git a/core/src/containers/values-dashboard-tools/ValuesDashboardTools.tsx b/core/src/containers/values-dashboard-tools/ValuesDashboardTools.tsx index 6d0fe06..0ea9987 100644 --- a/core/src/containers/values-dashboard-tools/ValuesDashboardTools.tsx +++ b/core/src/containers/values-dashboard-tools/ValuesDashboardTools.tsx @@ -50,11 +50,10 @@ const ValueDashboardTools: FC = ({ const renderSortLabel = ({ label }: Option) => `Sort by ${label.toLowerCase()}` + const nextSortDirection = sortDirection === "asc" ? "desc" : "asc" + const handleSortDirectionChange = () => { - setSearchParams( - searchParamsKeys.sortDirection, - sortDirection === "asc" ? "desc" : "asc" - ) + setSearchParams(searchParamsKeys.sortDirection, nextSortDirection) } const sortIcon = @@ -72,12 +71,13 @@ const ValueDashboardTools: FC = ({ ) return ( -
+
= ({ value={country} onChange={handleSelectChange(searchParamsKeys.country)} className="flex-30" + data-testid="admin-dashboard-country-select" size="small" /> + return ( + + ) } export default forwardRef(Input) diff --git a/core/src/ui/select-with-search/SelectWithSearch.tsx b/core/src/ui/select-with-search/SelectWithSearch.tsx index 5379139..56fa75e 100644 --- a/core/src/ui/select-with-search/SelectWithSearch.tsx +++ b/core/src/ui/select-with-search/SelectWithSearch.tsx @@ -115,6 +115,7 @@ const SelectWithSearch: FC = ({ "select__container select-with-search", containerProps?.className )} + data-testid="select" >
= ({ className, isSelected && "selected" )} + data-testid="option" role="option" {...props} /> diff --git a/e2e/cypress/support/index.ts b/e2e/cypress/support/index.ts index 31a8a1c..09c699c 100644 --- a/e2e/cypress/support/index.ts +++ b/e2e/cypress/support/index.ts @@ -21,9 +21,7 @@ declare global { beforeEach(() => { cy.setCookie("client_id", "m0qqf9t91yatil3svh"); - cy.task("clearTestDatabase").then(() => { - cy.task("populateTestDatabase"); - }); + cy.task("clearTestDatabase").then(() => cy.task("populateTestDatabase")); }); afterEach(() => { diff --git a/e2e/cypress/tests/common/common.steps.ts b/e2e/cypress/tests/common/common.steps.ts index 48d229a..d5d6492 100644 --- a/e2e/cypress/tests/common/common.steps.ts +++ b/e2e/cypress/tests/common/common.steps.ts @@ -9,6 +9,10 @@ When("I submit form", () => { cy.get('button[type="submit"]:visible').click(); }); +When("I submit form inside of a modal", () => { + cy.getById("modal").get('button[type="submit"]').click(); +}); + Then("I should be navigated to {string} page", (urlSubstring: string) => { if (urlSubstring === "home") { cy.url().should("equal", "http://localhost:3000/"); diff --git a/e2e/cypress/tests/countries-dashboard/countries-dashboard.feature b/e2e/cypress/tests/countries-dashboard/countries-dashboard.feature index 3c4daf8..46bb9d4 100644 --- a/e2e/cypress/tests/countries-dashboard/countries-dashboard.feature +++ b/e2e/cypress/tests/countries-dashboard/countries-dashboard.feature @@ -69,14 +69,14 @@ Feature: Countries dashboard Scenario: New country can be created When I open new country form And I fill in needed country values - And I submit form + And I submit form inside of a modal Then I should see a new country in a table @feature Scenario: New country fields validation works as expected When I open new country form And I fill in only country name - And I submit form + And I submit form inside of a modal Then country id input should be highlighted indicating failed validation @feature @@ -95,7 +95,7 @@ Feature: Countries dashboard When I open first country options dropdown And I open edit country modal And I fill in new country name - And I submit form + And I submit form inside of a modal Then I should see changed country @feature diff --git a/e2e/cypress/tests/indicators-dashboard/indicators-dashboard.feature b/e2e/cypress/tests/indicators-dashboard/indicators-dashboard.feature index b2fd2dd..ac69a24 100644 --- a/e2e/cypress/tests/indicators-dashboard/indicators-dashboard.feature +++ b/e2e/cypress/tests/indicators-dashboard/indicators-dashboard.feature @@ -68,14 +68,14 @@ Feature: Indicator dashboard Scenario: New indicator can be created When I open new indicator form And I fill in needed values - And I submit form + And I submit form inside of a modal Then I should see a new indicator in a table @feature Scenario: New indicator fields validation works as expected When I open new indicator form And I fill in only name - And I submit form + And I submit form inside of a modal Then input should be highlighted indicating failed validation @feature @@ -94,7 +94,7 @@ Feature: Indicator dashboard When I open first indicator's options dropdown And I open edit indicator modal And I fill in new name - And I submit form + And I submit form inside of a modal Then I should see changed indicator @feature diff --git a/e2e/cypress/tests/values-dashboard/values-dashboard.feature b/e2e/cypress/tests/values-dashboard/values-dashboard.feature new file mode 100644 index 0000000..5b58d7b --- /dev/null +++ b/e2e/cypress/tests/values-dashboard/values-dashboard.feature @@ -0,0 +1,139 @@ +@page +Feature: Values dashboard + + Background: Values dashboard background + Given I am on values dashboard page + + @smoke + Scenario: Values dashboard content is displayed correctly + When the values dashboard page has finished loading + Then I should see dashboard title + And I should see dashboard subtitle + And I should see dashboard tools + And I should see dashboard table + + @feature + Scenario Outline: Sorting functionality works as expected + When I open sort dropdown + And I select value sorting by "" + Then I should see values sorted by "" + + Examples: + | criteria | + | id | + | indicator id | + | country id | + | date of update | + + @feature + Scenario Outline: Sorting direction switching works as expected + When I choose "" sort direction + Then I should see rows sorted in "" order + + Examples: + | direction | + | asc | + | desc | + + @feature + Scenario Outline: Filtering by indicator works as expected + When I open indicator dropdown + And I select "" + Then I should see only values associated with "" indicator + + Examples: + | indicator | + | all indicators | + | gdp | + | population | + + @feature + Scenario Outline: Filtering by country works as expected + When I open country dropdown + And I select "" + Then I should see only values associated with "" country + + Examples: + | indicator | + | all countries | + | united kingdom | + | france | + + @feature + Scenario: Country select search works as expected + When I open country dropdown + And I type "France" in country select search field + Then 2 options should be displayed in opened select dropdown + + @feature + Scenario: Indicator select search works as expected + When I open indicator dropdown + And I type "Land area" in indicator select search field + Then 2 options should be displayed in opened select dropdown + + @feature + Scenario: Data refresh works as expected + When I refresh values table data + Then I should see refreshed data + + @feature + Scenario: Filters clearing works as expected + Given I already applied some value filters + When I clear filters + Then I should see only initial value filters being applied + + @feature + Scenario: New value can be created + When I open new value form + And I fill in needed for a new value data + And I submit form inside of a modal + Then new value should be created + + @feature + Scenario: Value editing works as expected + When I open first value options dropdown + And I open edit value modal + And I fill in new year + And I submit form inside of a modal + Then I should see changed value + + @feature + Scenario: Full value information can be opened + When I open first value options dropdown + And I open more information modal + Then I should see full value information + + @feature + Scenario: Value can be deleted + When I open first value options dropdown + And I click delete value + And I confirm action + Then deleted value should not be displayed in a table + + @feature + Scenario: Value selection works as expected + When I select value + And I unselect value + Then value should not be selected + + @feature + Scenario: Multiple values selection works as expected + When I select 3 first values + And I open first value options dropdown + And I click unselect all values + Then no values should be selected + + @feature + Scenario: Multiple values deleting works as expected + When I select 3 first values + And I open first value options dropdown + And I click delete selected values + And I confirm action + Then deleted values should not be displayed + + @feature + Scenario: New value fields validation works as expected + When I open new value form + And I fill in only year + And I submit form inside of a modal + Then value inputs should be highlighted indicating failed validation diff --git a/e2e/cypress/tests/values-dashboard/values-dashboard.steps.ts b/e2e/cypress/tests/values-dashboard/values-dashboard.steps.ts new file mode 100644 index 0000000..2b308a5 --- /dev/null +++ b/e2e/cypress/tests/values-dashboard/values-dashboard.steps.ts @@ -0,0 +1,213 @@ +import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor"; + +const criteriaUrlParamMap = { + id: "id", + "indicator id": "indicatorId", + "country id": "countryId", + "date of update": "updatedAt", +}; + +const indicatorUrlParamMap = { + population: "SP.POP.TOTL", + gdp: "NY.GDP.MKTP.CD", + "all indicators": "all", +}; + +const countryUrlParamMap = { + "united kingdom": "GBR", + france: "FRA", + "all countries": "all", +}; + +Given("I am on values dashboard page", () => { + cy.login(); + cy.visit( + "/admin/dashboard/values?indicator=all&country=all&sort=id&sortDirection=asc" + ); +}); + +When("the values dashboard page has finished loading", () => { + cy.getById("admin-dashboard-header").should("exist"); + cy.getById("admin-dashboard-table").should("exist"); +}); + +Then("I should see values sorted by {string}", (criteria: string) => { + cy.wait("@sort-requrest"); + cy.url().should("include", `sort=${criteriaUrlParamMap[criteria]}`); + cy.get("body").contains(`sort by ${criteria}`, { matchCase: false }); +}); + +When("I open indicator dropdown", () => { + cy.getById("admin-dashboard-indicator-select").click(); +}); + +Then( + "I should see only values associated with {string} indicator", + (indicator: string) => { + cy.url().should("include", `indicator=${indicatorUrlParamMap[indicator]}`); + cy.get("body").contains(indicator, { matchCase: false }); + } +); + +When("I open country dropdown", () => { + cy.getById("admin-dashboard-country-select").click(); +}); + +Then( + "I should see only values associated with {string} country", + (indicator: string) => { + cy.url().should("include", `country=${countryUrlParamMap[indicator]}`); + cy.get("body").contains(indicator, { matchCase: false }); + } +); + +When("I type {string} in country select search field", (value: string) => { + cy.getById("select").getById("input").type(value); +}); + +Then( + "{int} options should be displayed in opened select dropdown", + (numberOfOptions: number) => { + cy.getById("select") + .getById("option") + .should("have.length", numberOfOptions); + } +); + +When("I type {string} in indicator select search field", (value: string) => { + cy.getById("select").getById("input").type(value); +}); + +When("I refresh values table data", () => { + cy.intercept( + /admin\/dashboard\/values\?indicator=all&country=all&sort=id&sortDirection=asc&_rsc/ + ).as("refresh-request"); + cy.getById("admin-dashboard-refresh-button").click(); +}); + +Given("I already applied some value filters", () => { + cy.visit( + "/admin/dashboard/values?indicator=NY.GDP.MKTP.CD&country=GBR&sort=updatedAt&sortDirection=desc" + ); +}); + +Then("I should see only initial value filters being applied", () => { + cy.url().should("match", /admin\/dashboard\/values$/); +}); + +When("I open new value form", () => { + cy.getById("admin-dashboard-add-button").click(); +}); + +When("I fill in needed for a new value data", () => { + cy.getByName("countryId").type("FRA"); + cy.getByName("indicatorId").type("SP.POP.TOTL"); + cy.getByName("value").type("111555111"); + cy.getByName("year").type("555111555"); +}); + +Then("new value should be created", () => { + cy.get("body").contains("Value was created successffully"); +}); + +When("I open first value options dropdown", () => { + cy.getById("value-options-button").first().click(); +}); + +When("I open edit value modal", () => { + cy.getById("dropdown") + .contains("edit value", { matchCase: false }) + .click({ scrollBehavior: "center" }); +}); + +When("I fill in new year", () => { + cy.getByName("year").clear().type("1000"); +}); + +Then("I should see changed value", () => { + cy.get("body").contains("1 000"); + cy.get("body").contains("Value was updated successffully"); +}); + +Then("I should see full value information", () => { + cy.getById("value-modal-id").should("exist").should("be.visible"); + cy.getById("value-modal-indicator-id").should("exist").should("be.visible"); + cy.getById("value-modal-country-id").should("exist").should("be.visible"); + cy.getById("value-modal-country-value").should("exist").should("be.visible"); + cy.getById("value-modal-country-year").should("exist").should("be.visible"); + cy.getById("value-modal-updatedAt").should("exist").should("be.visible"); + cy.getById("value-modal-createdAt").should("exist").should("be.visible"); + cy.getById("value-modal-edit-button").should("exist").should("be.visible"); +}); + +When("I click delete value", () => { + cy.getById("table-row").its("length").as("rows"); + cy.getById("dropdown") + .contains("delete value", { matchCase: false }) + .click({ scrollBehavior: "center" }); + cy.intercept("DELETE", /api\/admin\/values/).as("delete-values-request"); +}); + +Then("deleted value should not be displayed in a table", () => { + cy.wait("@delete-values-request"); + cy.get("@rows").should("not.eq", cy.getById("table-row").its("length")); +}); + +When("I select value sorting by {string}", (criteria: string) => { + cy.intercept("GET", new RegExp("sort=" + criteriaUrlParamMap[criteria])).as( + "sort-requrest" + ); + cy.getById("select").contains(criteria, { matchCase: false }).click(); +}); + +When("I select value", () => { + cy.getById("select-button").first().click(); +}); + +When("I unselect value", () => { + cy.getById("select-button").first().click(); +}); + +Then("value should not be selected", () => { + cy.getById("table-row").should("not.have.class", "selected"); +}); + +When("I select {int} first values", (valuesToSelect: number) => { + for (let i = 0; i < valuesToSelect; i++) { + cy.getById("select-button").eq(i).click({ waitForAnimations: false }); + } +}); + +When("I click unselect all values", () => { + cy.getById("dropdown") + .contains("clear selection", { matchCase: false }) + .click({ scrollBehavior: "center" }); +}); + +Then("no values should be selected", () => { + cy.getById("admin-dashboard-table").get(".selected").should("not.exist"); +}); + +When("I click delete selected values", () => { + cy.getById("table-row").its("length").as("rows"); + cy.getById("dropdown") + .contains("delete selected", { matchCase: false }) + .click({ scrollBehavior: "center" }); + + cy.intercept("DELETE", /api\/admin\/values/).as("delete-values-request"); +}); + +Then("deleted values should not be displayed", () => { + cy.wait("@delete-values-request"); + cy.get("@rows").should("not.eq", cy.getById("table-row").its("length")); +}); + +When("I fill in only year", () => { + cy.getByName("year").type("2015"); +}); + +Then("value inputs should be highlighted indicating failed validation", () => { + cy.getByName("value").should("have.class", "error"); + cy.getByName("countryId").should("have.class", "error"); + cy.getByName("indicatorId").should("have.class", "error"); +});