diff --git a/.env b/.env index b6f79a2..474cb1e 100644 --- a/.env +++ b/.env @@ -4,3 +4,7 @@ NEXT_PUBLIC_PROVIDER_NAME=acme NEXT_PUBLIC_PROVIDER_ID=572 NEXT_PUBLIC_SCIENTIST_API_VERSION=v2 + +# values to use for testing +TEST_SCIENTIST_USER=test@test.com +TEST_SCIENTIST_PW=!test1234 diff --git a/README.md b/README.md index df5fce7..896bfd2 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,18 @@ There are 2 types of Cypress tests, e2e & component. If you are creating an e2e test, it will live in the `cypress/e2e` directory. Component tests will need to be created in a directory called `cypress/component ` +#### Setup your Cypress env variables +- the Cypress suite requires an environment variable that should be stored in your `.env.local` and not committed to git. + - TEST_SESSION_COOKIE= + - to get the value for this variable, open your browser to your running app at `localhost:3000`. + - inspect the page + - click the "Application" tab + - click "Cookies" + - find the value for `next-auth.session-token` + - copy that value and paste it in the `TEST_SESSION_COOKIE` variable in your .env.local + - do not ever commit this value + - this value will need to be updated whenever the cookie expires, approximately once per month + ## Cutting a New Release A git tag should exist for every release. We use `release-it` to automate the coordination of package.json and git tag. diff --git a/cypress.config.js b/cypress.config.js index 2c7701f..5c69ddd 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,4 +1,6 @@ -const { defineConfig } = require("cypress"); +require('dotenv').config({ path: `.env.local`, override: true }) + +const { defineConfig } = require("cypress") module.exports = defineConfig({ component: { @@ -9,8 +11,14 @@ module.exports = defineConfig({ }, e2e: { + baseUrl: 'http://localhost:3000', + chromeWebSecurity: false, setupNodeEvents(on, config) { - // implement node event listeners here + config.env = { + ...process.env, + ...config.env + } + return config }, }, }); diff --git a/cypress/e2e/browsing.cy.js b/cypress/e2e/browsing.cy.js new file mode 100644 index 0000000..04af3f1 --- /dev/null +++ b/cypress/e2e/browsing.cy.js @@ -0,0 +1,35 @@ +describe('Browsing', () => { + it('completes a search from the home page and navigates to "/browse" with a blank query', () => { + // Start from the home/index page + cy.visit('/') + + // Find the search button and perform an empty search, which should lead to the browse page + cy.get('button.search-button').click() + + // The new url should include "/browse" + cy.url().should('include', '/browse') + + // The new url should not contain a query + cy.url().should('not.include', '?') + + // The search bar on the browse page should remain blank + cy.get('input.search-bar').should('have.value', '') + }) + + it('completes a search from the home page and navigates to "/browse" with a query term', () => { + // Start from the home/index page + cy.visit('/') + + // type an example search into the searchbar + cy.get('input.search-bar').type('next') + + // Press the search button + cy.get('button.search-button').click() + + // The new url should include "/browse" + cy.url().should('include', '/browse?=next') + + // The search bar on the browse page should have the text that was searched for + cy.get('input.search-bar').should('have.value', 'next') + }) +}) \ No newline at end of file diff --git a/cypress/e2e/index.cy.js b/cypress/e2e/index.cy.js deleted file mode 100644 index ac21748..0000000 --- a/cypress/e2e/index.cy.js +++ /dev/null @@ -1,12 +0,0 @@ -describe('The home page', () => { - it('should be able to reach the browse page', () => { - // Start from the home/index page - cy.visit('http://localhost:3000/') - - // Find the search button and perform an empty search, which should lead to the browse page - cy.get('button.search-button').click() - - // The new url should include "/browse" - cy.url().should('include', '/browse') - }) -}) \ No newline at end of file diff --git a/cypress/e2e/requests.cy.js b/cypress/e2e/requests.cy.js new file mode 100644 index 0000000..d5835a9 --- /dev/null +++ b/cypress/e2e/requests.cy.js @@ -0,0 +1,26 @@ +describe('Viewing all requests', () => { + it('does not show a request list if the user is logged out.', () => { + // Visit a protected route in order to allow cypress to set the cookie and mock the login + cy.visit("/requests") + + cy.get('div.alert-heading').contains('Unauthorized').then(() => { + cy.log("A logged out user is not able to view requests.") + }) + }) + + it("shows the user's request list if they are logged in.", () => { + // Call the custom cypress command to log in + cy.login(Cypress.env('TEST_SCIENTIST_USER'), Cypress.env('TEST_SCIENTIST_PW')) + + // Visit a protected route in order to allow cypress to set the cookie and mock the login + cy.visit("/requests") + + // check that either the requests are showing, or that the user has no requests + const requestListExists = cy.get('article.request-item').should('exist') || + cy.get('p.no-requests').contains('You do not have any requests yet.') + + requestListExists.then(() => { + cy.log('Successfully logged in and viewing request list') + }) + }) +}) \ No newline at end of file diff --git a/cypress/fixtures/session.json b/cypress/fixtures/session.json new file mode 100644 index 0000000..4683ca4 --- /dev/null +++ b/cypress/fixtures/session.json @@ -0,0 +1,9 @@ +{ + "user": { + "name": "Spongebob Squarepants", + "email": "spongebob@bikinibottom.com", + "image": "/path/to/mock/user.jpg" + }, + "expires": "3000-01-01T00:00:00.000Z", + "accessToken": "abcdefghijklmnopqrst" +} \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000..4816cc0 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,5 @@ +const dotenvPlugin = require('cypress-dotenv'); +module.exports = (on, config) => { + config = dotenvPlugin(config) + return config +} \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 66ea16e..f7d258b 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -22,4 +22,18 @@ // // // -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) \ No newline at end of file +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + + +// add a command to login that uses a session, so the user will remain logged in throughout the test file vs. needing to log in before each example. +// source: https://github.com/nextauthjs/next-auth/discussions/2053#discussioncomment-1191016 +Cypress.Commands.add('login', (username, password) => { + cy.session([username, password], () => { + cy.intercept("/api/auth/session", { fixture: "session.json" }).as("session"); + + // Set the cookie for cypress. + // It has to be a valid cookie so next-auth can decrypt it and confirm its validity. + // This cookie also may need to be refreshed intermittently if it expires + cy.setCookie("next-auth.session-token", Cypress.env('TEST_SESSION_COOKIE')); + }) +}) \ No newline at end of file diff --git a/package.json b/package.json index 768c001..0b0ae0d 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "cypress": "^12.1.0", + "cypress-dotenv": "^2.0.0", + "dotenv": "^16.0.3", "eslint": "8.25.0", "eslint-config-next": "12.3.1", "jest": "^29.3.1", diff --git a/yarn.lock b/yarn.lock index 099228b..14f35b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2452,6 +2452,15 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +cypress-dotenv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cypress-dotenv/-/cypress-dotenv-2.0.0.tgz#ed77f232de7473c0c29f997fa8ff8b00faa5be2f" + integrity sha512-MHCkWuTU51n/Q6LT2FgjNVZx/OmnrP3Jve7TUGZvSbjgGOpBOVZTbm3tomjvn+2S6HhloRXSAOmT5fY9pvWkpQ== + dependencies: + camelcase "^6.2.0" + dotenv-parse-variables "^2.0.0" + lodash.clonedeep "^4.5.0" + cypress@^12.1.0: version "12.3.0" resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.3.0.tgz#ae3fb0540aef4b5eab1ef2bcd0760caf2992b8bf" @@ -2536,7 +2545,7 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2721,6 +2730,18 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" +dotenv-parse-variables@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dotenv-parse-variables/-/dotenv-parse-variables-2.0.0.tgz#8bfd83842acdc9013c12d46b340df27ac6046a26" + integrity sha512-/Tezlx6xpDqR6zKg1V4vLCeQtHWiELhWoBz5A/E0+A1lXN9iIkNbbfc4THSymS0LQUo8F1PMiIwVG8ai/HrnSA== + dependencies: + debug "^4.3.1" + is-string-and-not-blank "^0.0.2" + +dotenv@^16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== dot-prop@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" @@ -4201,6 +4222,17 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string-and-not-blank@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/is-string-and-not-blank/-/is-string-and-not-blank-0.0.2.tgz#cd19eded2ca4a514f79ca528915f1fb28e5dd38a" + integrity sha512-FyPGAbNVyZpTeDCTXnzuwbu9/WpNXbCfbHXLpCRpN4GANhS00eEIP5Ef+k5HYSNIzIhdN9zRDoBj6unscECvtQ== + dependencies: + is-string-blank "^1.0.1" + +is-string-blank@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-string-blank/-/is-string-blank-1.0.1.tgz#866dca066d41d2894ebdfd2d8fe93e586e583a03" + integrity sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw== is-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" @@ -5002,6 +5034,11 @@ lodash-es@^4.17.15, lodash-es@^4.17.21: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"