diff --git a/TODO b/TODO index 8c1f8dc..56f6f90 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,6 @@ TESTS // TODO tests for tournament page: - map and table mounts in tournament page <- get data to send to map/table -//TODO data fetching tests -//TODO redo layer groups tests -//TODO write tests for contact form -//TODO dead link tests take too long, split them into separate tests per page ----------------------------------------------------------------- BUGS @@ -21,20 +17,18 @@ DESIGN CHANGES //TODO test entire site for english to french translations //TODO success and error messages into french - mailer //TODO font size on mobile screen -//TODO mobile navbar is creeping into the page by a few pixels when hidden - move it to the right a bit. It is easier to see in light mode +//TODO mobile navbar is creeping into the page by a few pixels when hidden - move it to the right a bit. It is easier to see in light mode. I cant see this on Linux //TODO logo for navbar and favicon //TODO mobile map needs improving ---------------------------------------------------------------- LOGIC -//TODO SRP for web and API data fetching //TODO error handling //TODO consider offering GraphQL support MISC ---------------------------------------------------------------- //TODO write logger (fullstack open examples) -//TODO SEO - next headers etc //TODO multi-language i18n support - https://nextjs.org/docs/app/building-your-application/routing/internationalization //TODO move smaller ui components into a new folder, and make them reusable - such as using generic prop names //TODO readme diff --git a/app/api/v1/tournaments/france/route.ts b/app/api/v1/tournaments/france/route.ts index afcd444..0ccd2c1 100644 --- a/app/api/v1/tournaments/france/route.ts +++ b/app/api/v1/tournaments/france/route.ts @@ -14,7 +14,6 @@ export async function GET() { try { const client = await clientPromise; const db = client.db("tournamentsFranceDB"); - const results = await dateOrderingFrance(db); const data = results.map(({ _id, ...rest }) => ({ id: _id, diff --git a/app/tournois/page.tsx b/app/tournois/page.tsx index 03c69d1..c6cc6e9 100644 --- a/app/tournois/page.tsx +++ b/app/tournois/page.tsx @@ -23,9 +23,7 @@ const getTournaments = async () => { try { const client = await clientPromise; const db = client.db("tournamentsFranceDB"); - const data = await dateOrderingFrance(db); - return JSON.stringify(data); } catch (error) { throw new Error("Error fetching tournament data"); diff --git a/components/ContactForm.tsx b/components/ContactForm.tsx index d80148a..5902691 100644 --- a/components/ContactForm.tsx +++ b/components/ContactForm.tsx @@ -17,6 +17,7 @@ const ContactForm = () => { className={`${ responseMessage.isSuccessful ? "text-green-600" : "text-red-600" } italic`} + data-cy="info-message" > {responseMessage.message}

@@ -51,6 +52,7 @@ const ContactForm = () => { className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light" placeholder="nom@exemple.com" required + data-cy="email-input" />
@@ -68,6 +70,7 @@ const ContactForm = () => { className="block p-3 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 shadow-sm focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 dark:shadow-sm-light" placeholder="Le motif de ma demande" required + data-cy="subject-input" />
@@ -84,12 +87,15 @@ const ContactForm = () => { rows={6} className="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg shadow-sm border border-gray-300 focus:ring-primary-500 focus:border-primary-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500" placeholder="Détaillez ici votre demande..." + data-cy="message-input" + required >
diff --git a/cypress/component/utils.cy.ts b/cypress/component/utils.cy.ts deleted file mode 100644 index c3648db..0000000 --- a/cypress/component/utils.cy.ts +++ /dev/null @@ -1,55 +0,0 @@ -// import { Tournament } from "@/types"; -// import getTournaments from "@/utils/getTournamentData"; -// import { createLayerGroups } from "@/utils/layerGroups"; -// - -// // TODO rewrite this test suite since I no longer use fetch for the map -// // TODO add tests for the API once it is active -// describe("Unit tests of utils directory", () => { -// describe("Retrieve tournament data from DB", () => { -// let response: Tournament[]; -// let results: Tournament[]; -// -// describe("France", () => { -// before(async () => { -// response = await getTournaments("france"); -// results = response.splice(0, 5); -// }); -// -// it("log first 5 results", () => { -// results.forEach((result) => cy.log(JSON.stringify(result))); -// }); -// -// it("check tournament urls are active", () => { -// results.forEach((result) => cy.request(result.url)); -// }); -// }); -// }); -// -// describe("Create layer groups for map markers", () => { -// let response: Tournament[]; -// let results: Tournament[]; -// describe("France", () => { -// const timeControls = [ -// { name: "Cadence Lente", colour: "green" }, -// { name: "Rapide", colour: "blue" }, -// { name: "Blitz", colour: "yellow" }, -// { name: "1h KO", colour: "red" }, -// ]; -// -// before(async () => { -// response = await getTournaments("france"); -// results = response.splice(0, 5); -// }); -// -// it("generate layer groups", () => { -// const result = timeControls.map((timeControl) => { -// return createLayerGroups(timeControl.name, timeControl.colour, { -// tournamentData: results, -// }); -// }); -// cy.wrap(result.length).should("be.greaterThan", 0); -// }); -// }); -// }); -// }); diff --git a/cypress/e2e/contactForm.cy.ts b/cypress/e2e/contactForm.cy.ts new file mode 100644 index 0000000..7e6e068 --- /dev/null +++ b/cypress/e2e/contactForm.cy.ts @@ -0,0 +1,50 @@ +describe("Contact form", () => { + const email = "test@test.com"; + const invalidEmail = "test@"; + const subject = "Test Subject"; + const message = "Test message"; + + describe("Invalid data", () => { + beforeEach(() => { + cy.visit("/contactez-nous"); + }); + + it("invalid email address", () => { + cy.getByData("email-input").type(invalidEmail); + cy.getByData("subject-input").type(subject); + cy.getByData("message-input").type(message); + cy.getByData("submit-button").click(); + cy.getByData("info-message").should("have.text", ""); + }); + + it("missing subject", () => { + cy.getByData("email-input").type(email); + cy.getByData("message-input").type(message); + cy.getByData("submit-button").click(); + cy.getByData("info-message").should("have.text", ""); + }); + + it("missing message", () => { + cy.getByData("email-input").type(email); + cy.getByData("subject-input").type(subject); + cy.getByData("submit-button").click(); + cy.getByData("info-message").should("have.text", ""); + }); + }); + + describe("Valid data", () => { + beforeEach(() => { + cy.visit("/contactez-nous"); + }); + + it("valid input", () => { + cy.getByData("email-input").type(email); + cy.getByData("subject-input").type(subject); + cy.getByData("message-input").type(message); + cy.getByData("submit-button").click().should("be.disabled"); + cy.wait(3000); + cy.getByData("info-message").contains("Thank you"); + cy.getByData("submit-button").click().should("be.not.disabled"); + }); + }); +}); diff --git a/cypress/e2e/data.cy.tsx b/cypress/e2e/data.cy.ts similarity index 100% rename from cypress/e2e/data.cy.tsx rename to cypress/e2e/data.cy.ts diff --git a/cypress/e2e/links.cy.ts b/cypress/e2e/links.cy.ts index 72ffcd8..fe52583 100644 --- a/cypress/e2e/links.cy.ts +++ b/cypress/e2e/links.cy.ts @@ -1,11 +1,27 @@ -describe("Test all links", () => { +describe("Test links", () => { const pages = ["tournois", "qui sommes-nous", "contactez-nous"]; const navLinkToSlug = (navLink: string) => { return navLink.replace(/\s+/g, "-"); }; - it("Check navbar links point to correct pathname as a slug", () => { + const testLinks = () => { + cy.get("a:not([href*='mailto:']):not(nav a):not(footer a)").each((link) => { + if ( + !link + .prop("href") + .includes("http://www.echecs.asso.fr/FicheTournoi.aspx") // ignore every external FFE tournament link + ) { + cy.request({ + url: link.prop("href"), + failOnStatusCode: false, + }); + cy.log(link.prop("href")); + } + }); + }; + + it("navbar links point to correct pathname as a slug", () => { cy.viewport(600, 600); cy.visit("/"); pages.forEach((page) => { @@ -16,22 +32,41 @@ describe("Test all links", () => { }); }); - it("Check dead links", () => { - pages.forEach((page) => { - cy.visit(`/${navLinkToSlug(page)}`); - cy.get("a:not([href*='mailto:']").each((link) => { - if ( - !link - .prop("href") - .includes("http://www.echecs.asso.fr/FicheTournoi.aspx") // ignore every external FFE tournament link - ) { + // TODO consider doing .each from pages array once links are on each page + // TODO at present, the commented out tests have no links so fail + it("homepage dead links", () => { + cy.visit("/"); + testLinks(); + }); + + it("tournois dead links", () => { + cy.visit("/tournois"); + testLinks(); + }); + + // it("qui-sommes-nous dead links", () => {s + // cy.visit("/qui-sommes-nous"); + // testLinks(); + // }); + // + // it("contactez-nous dead links", () => { + // cy.visit("/contactez-nous"); + // testLinks(); + // }); + + //TODO consider 5 random links rather than first 5 + it("first 5 tournament external links", () => { + cy.visit("/tournois"); + cy.get("a:not([href*='mailto:']):not(nav a):not(footer a)").each( + (link, index) => { + if (index < 5) { cy.request({ url: link.prop("href"), failOnStatusCode: false, }); cy.log(link.prop("href")); } - }); - }); + } + ); }); }); diff --git a/cypress/e2e/navbar.cy.tsx b/cypress/e2e/navbar.cy.ts similarity index 100% rename from cypress/e2e/navbar.cy.tsx rename to cypress/e2e/navbar.cy.ts diff --git a/cypress/e2e/scroll.cy.tsx b/cypress/e2e/scroll.cy.ts similarity index 100% rename from cypress/e2e/scroll.cy.tsx rename to cypress/e2e/scroll.cy.ts diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 698b01a..22ec2e4 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,3 +1,13 @@ +declare namespace Cypress { + interface Chainable { + getByData(dataTestAttribute: string): Chainable>; + } +} + +Cypress.Commands.add("getByData", (selector) => { + return cy.get(`[data-cy=${selector}]`); +}); + /// // *********************************************** // This example commands.ts shows you how to @@ -34,4 +44,4 @@ // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable // } // } -// } \ No newline at end of file +// }