diff --git a/.env b/.env
deleted file mode 100644
index 4a10ac9..0000000
--- a/.env
+++ /dev/null
@@ -1,8 +0,0 @@
-# reference https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser
-# to learn about the NEXT_PUBLIC prefix
-
-NEXT_PUBLIC_PROVIDER_NAME=phenovista
-NEXT_PUBLIC_PROVIDER_ID=12527
-NEXT_PUBLIC_SCIENTIST_API_VERSION=v2
-NEXT_PUBLIC_WEBHOOK_URL=http://ss-mailer/webstore
-NEXT_PUBLIC_APP_BASE_URL=https://www.phenovista.vercel.app
diff --git a/README.md b/README.md
index 7890bf5..5bca77e 100644
--- a/README.md
+++ b/README.md
@@ -174,14 +174,16 @@ 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 `
#### Cypress ENV Variables
-- the Cypress suite requires an environment variable that should be stored in your `.env.development` and not committed to git.
+- the Cypress suite requires an environment variable that should be stored in your `.env` 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`.
+ - to get the value for this variable, open your browser to your running app at `localhost:3000`
+ - sign in
- inspect the page
- - click the "Application" tab
+ - Chrome: click the "Application" tab
+ - Firefox: click the "Storage" 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.development
+ - copy that value and paste it in the `TEST_SESSION_COOKIE` variable in your `.env`
- do not ever commit this value
- this value will need to be updated whenever the cookie expires, approximately once per month
diff --git a/cypress.config.js b/cypress.config.js
index 76c23b9..59795e0 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -11,6 +11,7 @@ module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
chromeWebSecurity: false,
+ defaultCommandTimeout: 10000,
setupNodeEvents(on, config) {
config = dotenvFlowPlugin(config)
config.env = {
@@ -21,14 +22,19 @@ module.exports = defineConfig({
},
},
env: {
- TEST_SCIENTIST_USER: 'test@test.com',
+ CYPRESS_SEARCH_QUERY: 'test',
+ NEXT_PUBLIC_PROVIDER_ID: process.env.NEXT_PUBLIC_PROVIDER_ID,
+ NEXT_PUBLIC_PROVIDER_NAME: process.env.NEXT_PUBLIC_PROVIDER_NAME,
+ NEXT_PUBLIC_TOKEN: process.env.NEXT_PUBLIC_TOKEN,
TEST_SCIENTIST_PW: '!test1234',
NEXT_PUBLIC_PROVIDER_NAME: 'phenovista',
- NEXT_PUBLIC_PROVIDER_ID: '12527'
+ NEXT_PUBLIC_PROVIDER_ID: '12527',
+ TEST_SCIENTIST_USER: 'test@test.com',
+ TEST_SESSION_COOKIE: process.env.TEST_SESSION_COOKIE,
},
reporter: 'junit',
reporterOptions: {
mochaFile: 'cypress/results/results-[hash].xml',
toConsole: true,
},
-});
+})
diff --git a/cypress/e2e/browse.cy.js b/cypress/e2e/browse.cy.js
index dd0e64f..aee5320 100644
--- a/cypress/e2e/browse.cy.js
+++ b/cypress/e2e/browse.cy.js
@@ -19,10 +19,10 @@ describe('Browsing', () => {
emptyFixture: 'services/no-wares.json',
},
]
-
+
beforeEach(() => {
// Intercept the responses from the endpoint to view all requests.
- // Even though this is to the same endpoint, the call happens on each page twice,
+ // Even though this is to the same endpoint, the call happens on each page twice,
// once when the page loads with all the wares, and again after any search is performed.
// this makes it necessary to create an intercept for each time the call is made.
intercepts.forEach((intercept) => {
@@ -103,39 +103,4 @@ describe('Browsing', () => {
})
})
})
-
- describe('from the home page', () => {
- beforeEach(() => {
- wares = true
- // Intercept the api call being made on the homepage
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useAllWares',
- requestURL: `/providers/${Cypress.env('NEXT_PUBLIC_PROVIDER_ID')}/wares.json`,
- data: wares,
- defaultFixture: 'services/wares.json',
- loading,
- error
- })
- cy.visit('/')
- })
-
- context('a search is completed successfully and', () => {
- it('navigates to "/browse" with a blank query', () => {
- cy.get('button.search-button').click()
- cy.url().should('include', '/browse')
- cy.url().should('not.include', '?')
- cy.get('input.search-bar').should('have.value', '')
- cy.get(".card[data-cy='item-card']").should('be.visible')
- })
-
- it('navigates to "/browse" with a query term', () => {
- cy.get('input.search-bar').type('test')
- cy.get('button.search-button').click()
- cy.url().should('include', '/browse?q=test')
- cy.get('input.search-bar').should('have.value', 'test')
- cy.get(".card[data-cy='item-card']").should('be.visible')
- })
- })
- })
-})
\ No newline at end of file
+})
diff --git a/cypress/e2e/home.cy.js b/cypress/e2e/home.cy.js
index cff47a0..1d967bf 100644
--- a/cypress/e2e/home.cy.js
+++ b/cypress/e2e/home.cy.js
@@ -1,67 +1,120 @@
-describe('Viewing Home page', () => {
+describe('Navigating to the home page', () => {
// declare variables that can be used to change how the response is intercepted.
- let loading
+ let data = 'services/wares.json'
let error
- let featuredServices
beforeEach(() => {
- // Intercept the response from the endpoint to view all requests
cy.customApiIntercept({
- action: 'GET',
alias: 'useAllWares',
- requestURL: `/providers/${Cypress.env('NEXT_PUBLIC_PROVIDER_ID')}/wares.json`,
- data: featuredServices,
- defaultFixture: 'services/wares.json',
- emptyFixture: 'services/no-wares.json',
- loading,
- error
+ data,
+ error,
+ requestURL: '/wares.json?per_page=2000',
})
+
cy.visit('/')
})
-
- context('featured services list is loading', () => {
- before(() => {
- loading = true
+ describe('renders a search bar', () => {
+ it('with no query', () => {
+ cy.get("form[data-cy='search-bar']").should('exist').then(() => {
+ cy.log('Search bar renders successfully.')
+ })
})
- it('should show 3 placeholder cards loading', () => {
- cy.get('p.placeholder-glow').should('be.visible').then(() => {
- cy.log('Loading text displays correctly.')
+
+ context('able to navigate to "/browse"', () => {
+ const testSetup = ({ data, defaultFixture, requestURL }) => {
+ cy.customApiIntercept({
+ alias: 'useFilteredWares',
+ data,
+ error,
+ requestURL,
+ })
+ }
+
+ it('with a blank query', () => {
+ testSetup({
+ data: 'services/wares.json',
+ requestURL: '/wares.json?per_page=2000&q=',
+ })
+
+ cy.get('button.search-button').click()
+ cy.url().should('include', '/browse')
+ cy.url().should('not.include', '?')
+ cy.get('input.search-bar').should('have.value', '')
+ cy.get(".card[data-cy='item-card']").should('be.visible')
+ })
+
+ it('with a valid query term', () => {
+ testSetup({
+ data: 'services/filtered-wares.json',
+ requestURL: `/wares.json?per_page=2000&q=${Cypress.env('CYPRESS_SEARCH_QUERY')}`,
+ })
+
+ cy.get('input.search-bar').type(Cypress.env('CYPRESS_SEARCH_QUERY'))
+ cy.get('button.search-button').click()
+ cy.url().should('include', `/browse?q=${Cypress.env('CYPRESS_SEARCH_QUERY')}`)
+ cy.get('input.search-bar').should('have.value', Cypress.env('CYPRESS_SEARCH_QUERY'))
+ cy.get(".card[data-cy='item-card']").should('be.visible')
+ })
+
+ it('with an invalid query term', () => {
+ const invalidQuery = 'asdfghjk'
+ testSetup({
+ data: 'services/no-wares.json',
+ requestURL: `/wares.json?per_page=2000&q=${invalidQuery}`,
+ })
+
+ cy.get('input.search-bar').type(invalidQuery)
+ cy.get('button.search-button').click()
+ cy.url().should('include', `/browse?q=${invalidQuery}`)
+ cy.get('input.search-bar').should('have.value', invalidQuery)
+ cy.get("p[data-cy='no-results']").should('contain', `Your search for ${invalidQuery} returned no results`)
})
})
})
- context('error while making a request to the api', () => {
- before(() => {
- loading = false
- error = true
- })
- it('should show an error message.', () => {
- cy.get("div[role='alert']").should('be.visible').then(() => {
- cy.log('Successfully hits an error.')
+ describe('renders a text box', () => {
+ it('showing the about text.', () => {
+ cy.get("section[data-cy='about-us-section']").should('exist').then(() => {
+ cy.log('Abouttext renders successfully.')
})
})
})
- context('home page components are loading successfully, &', () => {
- before(() => {
- featuredServices = true
- error = false
- })
- it('should show the search bar.', () => {
- cy.get("form[data-cy='search-bar']").should('exist').then(() => {
- cy.log('Search bar renders successfully.')
+ describe('makes a call to the api', () => {
+ context('which when given an invalid access token', () => {
+ before(() => {
+ data = undefined
+ error = {
+ body: {
+ message: 'No access token provided.',
+ },
+ statusCode: 403,
+ }
+ })
+
+ it('shows an error message', () => {
+ cy.get("div[role='alert']").should('be.visible').then(() => {
+ cy.log('Successfully hits an error.')
+ })
+ cy.get("div[role='alert']").contains('No access token provided.')
})
})
- it('should show the about text.', () => {
- cy.get("section[data-cy='about-us-section']").should('exist').then(() => {
- cy.log('Abouttext renders successfully.')
+
+ context('which when returns no error or data', () => {
+ it('shows 3 placeholder cards loading', () => {
+ cy.get('p.placeholder-glow').should('have.length', 3).then(() => {
+ cy.log('Loading text displays correctly.')
+ })
})
})
- it('should show the featured services cards.', () => {
- cy.get("div[data-cy='item-group']").should('exist').then(() => {
- cy.log('Status bar renders successfully.')
+
+ context('which when returns data', () => {
+ it('shows the featured services cards', () => {
+ cy.get("div[data-cy='item-group']").should('exist').then(() => {
+ cy.log('Status bar renders successfully.')
+ })
})
})
})
-})
\ No newline at end of file
+})
diff --git a/cypress/e2e/request.cy.js b/cypress/e2e/request.cy.js
index aa83b90..a03b39f 100644
--- a/cypress/e2e/request.cy.js
+++ b/cypress/e2e/request.cy.js
@@ -1,12 +1,9 @@
-import useOneRequestResponseBody from '../fixtures/one-request/request.json'
-
-describe.skip('Viewing one request', () => {
- // TODO: currently this uses a real request uuid, which would allow it to visit a route that actually existed.
- // since the routes are generated dynamically, we will need to mock the next router in order to generate a route for a fake request w/ mock uuid within the test
- // this test should remain skipped until the above is done since it runs as a regular e2e vs e2e with mocked data
- // Existing ticket to complete this test: https://github.com/scientist-softserv/webstore/issues/218
- let uuid = useOneRequestResponseBody.uuid
+import {
+ requestUuid as uuid,
+ requestPageApiCalls,
+} from '../support/e2e'
+describe('Viewing one request', () => {
describe('as a logged out user', () => {
it('should show an error message.', () => {
cy.visit(`/requests/${uuid}`)
@@ -17,99 +14,128 @@ describe.skip('Viewing one request', () => {
})
describe('as a logged in user', () => {
- // declare variables that can be used to change how the response is intercepted.
- let request
- let proposals
- let messages
- let files
- let loading
- let error
+ let apiCalls = Object.assign({}, requestPageApiCalls)
beforeEach(() => {
- // Call the custom cypress command to log in
cy.login(Cypress.env('TEST_SCIENTIST_USER'), Cypress.env('TEST_SCIENTIST_PW'))
-
- // Intercept the response from the endpoint to view one request
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useOneRequest',
- requestURL: `/quote_groups/${uuid}.json`,
- data: request,
- dataFixture: 'one-request/request.json',
- emptyDataFixture: 'empty.json',
- loading,
- error
- })
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useAllSOWs',
- requestURL: `/quote_groups/${uuid}/proposals.json`,
- data: proposals,
- dataFixture: 'one-request/proposals.json',
- emptyDataFixture: 'empty.json',
- loading,
- error
- })
-
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useAllMessages',
- requestURL: `/quote_groups/${uuid}/messages.json`,
- data: messages,
- dataFixture: 'one-request/messages.json',
- emptyDataFixture: 'empty.json',
- loading,
- error
- })
-
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useAllFiles',
- requestURL: `/quote_groups/${uuid}/notes.json`,
- data: files,
- dataFixture: 'one-request/notes.json',
- emptyDataFixture: 'empty.json',
- loading,
- error
+ Object.entries(apiCalls).forEach((item) => {
+ cy.customApiIntercept(item[1])
})
cy.visit(`/requests/${uuid}`)
})
- context('request is loading', () => {
- before(() => {
- loading = true
- })
- it('should show a loading spinner.', () => {
- cy.get("[aria-label='tail-spin-loading']").should('be.visible').then(() => {
- cy.log('Loading spinner displays correctly.')
+ afterEach(() => {
+ // in order for the tests to not be order dependent, we need to reset the apiCalls object to the original state
+ apiCalls = Object.assign({}, requestPageApiCalls)
+ })
+
+ describe('makes a call to the api', () => {
+ context('which when given an invalid uuid', () => {
+ before(() => {
+ apiCalls['useOneRequest'] = {
+ ...apiCalls['useOneRequest'],
+ data: undefined,
+ error: {
+ body: {
+ message: 'Quote Group Not Found',
+ },
+ statusCode: 404,
+ },
+ }
+ })
+
+ it('returns an error message', () => {
+ cy.get("div[role='alert']").should('be.visible').then(() => {
+ cy.log('Successfully hits an error.')
+ })
+ cy.get("div[role='alert']").contains('Quote Group Not Found')
})
})
- })
- describe('request page components are loading successfully, &', () => {
- context('the request page', () => {
+ context('which when returns undefined error and data values', () => {
before(() => {
- loading =
- request = true
- proposals = true
- messages = true
- files = true
+ Object.entries(apiCalls).forEach(([key, value]) => {
+ apiCalls[key] = {
+ ...value,
+ data: undefined,
+ error: undefined,
+ }
+ })
+ })
+
+ it('shows a loading spinner.', () => {
+ cy.get("[aria-label='tail-spin-loading']").should('be.visible').then(() => {
+ cy.log('Loading spinner displays correctly.')
+ })
})
+ })
- it("should show the request stats section.", () => {
- cy.get('div.request-stats-card').should('exist').then(() => {
+ describe('which when returns request data', () => {
+ it('shows the request stats section', () => {
+ cy.get('div.request-stats.card').should('exist').then(() => {
cy.log('Request stats section renders successfully.')
})
})
- it("should show the status bar.", () => {
+ it('shows the status bar', () => {
cy.get("div[data-cy='status-bar']").should('exist').then(() => {
cy.log('Status bar renders successfully.')
})
})
- // TODO: add tests to confirm that messages, files, additional info, document sections all show correctly.
+
+ context('with messages', () => {
+ before(() => {
+ apiCalls['useMessages'] = {
+ ...apiCalls['useMessages'],
+ data: 'one-request/messages/index.json',
+ }
+ })
+
+ it('displays the messages', () => {
+ cy.get('div.card-body p.card-text')
+ .contains('this is a message from the customer')
+ .should('be.visible')
+ })
+ })
+
+ context('with documents', () => {
+ before(() => {
+ apiCalls['useAllSOWs'] = {
+ ...apiCalls['useAllSOWs'],
+ data: 'one-request/sows/index.json',
+ }
+ apiCalls['getAllPOs'] = {
+ ...apiCalls['getAllPOs'],
+ data: 'one-request/pos/index.json',
+ }
+ })
+
+ it('displays the documents', () => {
+ cy.get('div.document').should('have.length', 2)
+ cy.get('div.badge').contains('SOW').should('be.visible')
+ cy.get('div.badge').contains('PO').should('be.visible')
+ })
+ })
+
+ context('with files', () => {
+ before(() => {
+ apiCalls['useFiles'] = {
+ ...apiCalls['useFiles'],
+ data: 'one-request/files/index.json',
+ }
+ })
+
+ it('displays the files', () => {
+ cy.get('div.actions-group')
+ .contains('View Files')
+ .click()
+ cy.get('div#document-tabs-tabpane-files')
+ .contains('downtown.jpg')
+ .should('be.visible')
+ })
+ })
})
})
})
-})
\ No newline at end of file
+})
diff --git a/cypress/e2e/requests.cy.js b/cypress/e2e/requests.cy.js
index 9217f4d..4b32557 100644
--- a/cypress/e2e/requests.cy.js
+++ b/cypress/e2e/requests.cy.js
@@ -1,108 +1,103 @@
-import { scientistApiBaseURL } from '../support/e2e'
-
describe('Viewing all requests', () => {
describe('as a logged out user', () => {
- it('should show an error message.', () => {
- // Visit a protected route in order to allow cypress to set the cookie and mock the login
+ it('shows an error message.', () => {
cy.visit('/requests')
cy.get('div.alert-heading').contains('Unauthorized').then(() => {
cy.log('A logged out user is not able to view requests.')
})
})
})
-
+
describe('as a logged in user', () => {
// declare variables that can be used to change how the response is intercepted.
- let requestList
- let loading
+ let data
let error
beforeEach(() => {
- // Call the custom cypress command to log in
cy.login(Cypress.env('TEST_SCIENTIST_USER'), Cypress.env('TEST_SCIENTIST_PW'))
- // Intercept the response from the endpoint to view all requests
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useAllRequests',
- requestURL: `/quote_groups/mine.json`,
- data: requestList,
- defaultFixture: 'all-requests/requests.json',
- emptyFixture: 'all-requests/no-requests.json',
- loading,
- error
- })
- // Intercept the response from the endpoint that gets the default ware ID
- cy.customApiIntercept({
- action: 'GET',
- alias: 'useDefaultWare',
- requestURL: `/wares.json?q=make-a-request`,
- defaultFixture: 'all-requests/make-a-request.json',
- error
- })
- cy.visit('/requests')
})
+ describe('makes a call to the api', () => {
+ beforeEach(() => {
+ cy.customApiIntercept({
+ alias: 'useAllRequests',
+ data,
+ error,
+ requestURL: `/quote_groups/mine.json`,
+ })
- context('request list is loading', () => {
- before(() => {
- loading = true
+ cy.visit('/requests')
})
- it('should show a loading spinner.', () => {
- cy.get("[aria-label='tail-spin-loading']").should('be.visible').then(() => {
- cy.log('Loading spinner displays correctly.')
+
+ context('which when given an invalid access token', () => {
+ before(() => {
+ error = {
+ body: {
+ message: 'No access token provided.',
+ },
+ statusCode: 403,
+ }
})
- })
- })
- context('error while making a request to the api', () => {
- before(() => {
- requestList = undefined
- loading = false
- error = true
- })
- it('should show an error message.', () => {
- cy.get("div[role='alert']").should('be.visible').then(() => {
- cy.log('Successfully hits an error.')
+ it('shows an error message.', () => {
+ cy.get("div[role='alert']").should('be.visible').then(() => {
+ cy.log('Successfully hits an error.')
+ })
+ cy.get("div[role='alert']").contains('No access token provided.')
})
})
- })
- describe('request components are loading successfully, &', () => {
- context('the user has requests', () => {
- before(() => {
- requestList = true
- error = false
- })
- it("should show the user's request list.", () => {
- cy.get('article.request-item').should('exist').then(() => {
- cy.log('Successfully viewing request list.')
+ context('which when returns undefined error and data values', () => {
+ it('shows a loading spinner.', () => {
+ cy.get("[aria-label='tail-spin-loading']").should('be.visible').then(() => {
+ cy.log('Loading spinner displays correctly.')
})
})
})
- context('the user has 0 requests', () => {
+ describe('which when returns a data object', () => {
before(() => {
- requestList = false
+ data = 'all-requests/requests.json'
+ cy.customApiIntercept({
+ alias: 'useDefaultWare',
+ data: 'all-requests/make-a-request.json',
+ error,
+ requestURL: '/wares.json',
+ })
+ })
+
+ it('renders the "New Request" button for the default service', () => {
+ cy.get("a[data-cy='linked-button']")
+ .should('have.attr', 'href', `/requests/new/make-a-request?id=123`)
+ .and('have.text', 'Initiate a New Request')
+ .then(() => {
+ cy.log('The