diff --git a/.github/workflows/asw2122.yml b/.github/workflows/asw2122.yml index 966cd14..afa3117 100644 --- a/.github/workflows/asw2122.yml +++ b/.github/workflows/asw2122.yml @@ -34,22 +34,9 @@ jobs: - run: npm ci - run: npm test - uses: codecov/codecov-action@v2 - # e2e-tests: - # needs: [unit-test-webapp, unit-test-restapi] - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v2 - # - uses: actions/setup-node@v2 - # with: - # node-version: 16 - # - run: npm --prefix webapp install - # - run: npm --prefix restapi install - # - run: npm --prefix webapp run build - # - run: npm --prefix webapp run test:e2e docker-push-webapp: name: Push webapp Docker Image to GitHub Packages runs-on: ubuntu-latest - # needs: [e2e-tests] needs: [unit-test-webapp] env: API_URI: http://${{ secrets.DEPLOY_HOST }}:5000 @@ -67,7 +54,6 @@ jobs: docker-push-restapi: name: Push restapi Docker Image to GitHub Packages runs-on: ubuntu-latest - # needs: [e2e-tests] needs: [unit-test-restapi] env: MAPBOX_API_KEY: ${{ secrets.MAPBOX_API_KEY }} @@ -122,3 +108,15 @@ jobs: heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_app_name: 'dede-es5a-restapi' #Must be unique in Heroku heroku_email: ${{secrets.HEROKU_EMAIL}} + e2e-tests: + needs: [restapi-heroku-deploy, webapp-heroku-deploy,deploy] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + - run: npm --prefix webapp install + - run: npm --prefix restapi install + - run: npm --prefix webapp run build + - run: npm --prefix webapp run test:e2e \ No newline at end of file diff --git a/docs/01_introduction_and_goals.adoc b/docs/01_introduction_and_goals.adoc index f0e65e3..0f65a71 100644 --- a/docs/01_introduction_and_goals.adoc +++ b/docs/01_introduction_and_goals.adoc @@ -20,6 +20,8 @@ The following diagram represents the functional requirements of the application. actor Client as client +left to right direction + package ArquiSocks { usecase "Add to cart" as addToCart usecase "Place order" as placeOrder @@ -29,7 +31,7 @@ package ArquiSocks { usecase "View products" as viewProducts } -cloud Database +database Database cloud SolidPod cloud MapsAPI diff --git a/webapp/e2e/features/distribution-centers.feature b/webapp/e2e/features/distribution-centers.feature new file mode 100644 index 0000000..2c64164 --- /dev/null +++ b/webapp/e2e/features/distribution-centers.feature @@ -0,0 +1,6 @@ +Feature: User consults distribution centers + +Scenario: User goes distribution centers page + Given A user who founds the site + When selects the distribution centers button + Then is redirected to the distribution centers page \ No newline at end of file diff --git a/webapp/e2e/features/signin.feature b/webapp/e2e/features/signin.feature new file mode 100644 index 0000000..9c8e7aa --- /dev/null +++ b/webapp/e2e/features/signin.feature @@ -0,0 +1,6 @@ +Feature: User log in + +Scenario: User goes to sign in page + Given A user who founds the site + When selects the Signin button on the top menu + Then is redirected to the sign in page \ No newline at end of file diff --git a/webapp/e2e/steps/add-to-cart.steps.ts b/webapp/e2e/steps/add-to-cart.steps.ts index 6b15a49..51a83cd 100644 --- a/webapp/e2e/steps/add-to-cart.steps.ts +++ b/webapp/e2e/steps/add-to-cart.steps.ts @@ -1,52 +1,64 @@ -import { defineFeature, loadFeature } from 'jest-cucumber'; -import puppeteer from "puppeteer"; +import { defineFeature, loadFeature } from 'jest-cucumber' +import puppeteer from 'puppeteer' -const feature = loadFeature('./features/add-to-cart.feature'); +const feature = loadFeature('./features/add-to-cart.feature') -let page: puppeteer.Page; -let browser: puppeteer.Browser; -jest.setTimeout(400000); +let page: puppeteer.Page +let browser: puppeteer.Browser +jest.setTimeout(400000) -defineFeature(feature, test => { - +defineFeature(feature, (test) => { beforeAll(async () => { browser = process.env.GITHUB_ACTIONS ? await puppeteer.launch() - : await puppeteer.launch({ headless: true }); - page = await browser.newPage(); + : await puppeteer.launch({ headless: true }) + page = await browser.newPage() await page - .goto("https://dede-es5a.herokuapp.com/", { - waitUntil: "networkidle0", + .goto('https://dede-es5a.herokuapp.com/', { + waitUntil: 'networkidle0', + timeout: 0, }) - .catch(() => {console.log("Error while testing")}); - }); - - test('User adds a product to the cart', ({given,when,then}) => { + .catch(() => { + console.log('Error while testing') + }) + }) + test('User adds a product to the cart', ({ given, when, then }) => { given('A user who founds the site', () => { console.log('Checking product added...') - }); - - when('He selects the product\'s size, he decides to add it to the cart', async () => { - //Se selecciona la talla en el combobox - await expect(page).toClick('div.MuiGrid-grid-xs-4:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(1)') - await expect(page).toClick('#menu- > div:nth-child(3) > ul:nth-child(1) > li:nth-child(1)') - - //Se hace click para añadir al carrito - await expect(page).toClick('div.MuiGrid-grid-xs-4:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > button:nth-child(2)') - }); - - then('A confirmation message should be shown in the screen and the product is added', async () => { - //Se muestra añadido - await expect(page).toClick('.MuiBadge-root > button:nth-child(1)') - await expect(page).toMatch('British') - }); + }) + + when( + "He selects the product's size, he decides to add it to the cart", + async () => { + await Promise.all([ + //Se selecciona la talla en el combobox + await expect(page).toClick( + 'div.MuiGrid-grid-xs-4:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(1)', + ), + await expect(page).toClick( + '#menu- > div:nth-child(3) > ul:nth-child(1) > li:nth-child(1)', + ), + //Se hace click para añadir al carrito + await expect(page).toClick( + 'div.MuiGrid-grid-xs-4:nth-child(1) > div:nth-child(1) > div:nth-child(3) > div:nth-child(1) > button:nth-child(2)', + ), + ]) + }, + ) + + then( + 'A confirmation message should be shown in the screen and the product is added', + async () => { + //Se muestra añadido + await expect(page).toClick('.MuiBadge-root > button:nth-child(1)') + await expect(page).toMatch('British') + }, + ) }) - afterAll(async ()=>{ + afterAll(async () => { browser.close() }) - -}); - +}) diff --git a/webapp/e2e/steps/distribution-centers.steps.ts b/webapp/e2e/steps/distribution-centers.steps.ts new file mode 100644 index 0000000..3bec7e2 --- /dev/null +++ b/webapp/e2e/steps/distribution-centers.steps.ts @@ -0,0 +1,46 @@ +import { defineFeature, loadFeature } from 'jest-cucumber'; +import puppeteer from "puppeteer"; + +const feature = loadFeature('./features/distribution-centers.feature'); + +let page: puppeteer.Page; +let browser: puppeteer.Browser; + +defineFeature(feature, test => { + + beforeAll(async () => { + browser = process.env.GITHUB_ACTIONS + ? await puppeteer.launch() + : await puppeteer.launch({ headless: true }); + page = await browser.newPage(); + + await page + .goto("https://dede-es5a.herokuapp.com", { + waitUntil: "networkidle0", + }) + .catch(() => { + // This is intentional + }); + }); + + test('User goes distribution centers page', ({given,when,then}) => { + jest.setTimeout(40000); + + given('A user who founds the site', () => { + // This is intentional + }); + + when('selects the distribution centers button', async () => { + await page.goto("https://dede-es5a.herokuapp.com/distributionCenters") //There is a problem with the button click, I have to do it this way + }); + + then('is redirected to the distribution centers page', async () => { + await expect(page).toMatch('Oviedo') + }); + }) + + afterAll(async ()=>{ + browser.close() + }) +}); + diff --git a/webapp/e2e/steps/signin.steps.ts b/webapp/e2e/steps/signin.steps.ts new file mode 100644 index 0000000..4a159fc --- /dev/null +++ b/webapp/e2e/steps/signin.steps.ts @@ -0,0 +1,50 @@ +import { defineFeature, loadFeature } from 'jest-cucumber' +import puppeteer from 'puppeteer' + +const feature = loadFeature('./features/signin.feature') + +let page: puppeteer.Page +let browser: puppeteer.Browser +jest.setTimeout(400000) + +defineFeature(feature, (test) => { + beforeAll(async () => { + browser = process.env.GITHUB_ACTIONS + ? await puppeteer.launch() + : await puppeteer.launch({ headless: true }) + page = await browser.newPage() + + await page + .goto('https://dede-es5a.herokuapp.com/', { + waitUntil: 'networkidle0', + timeout: 0 + }) + .catch(() => { + // This is intentional + }) + }) + + test('User goes to sign in page', ({ given, when, then }) => { + given('A user who founds the site', () => { + // This is intentional + }) + + when('selects the Signin button on the top menu', async () => { + await Promise.all([ + (await page.$x('/html/body/div[1]/div/header/div/div/div[5]/button')) + .at(0) + ?.click(), + (await page.$x('/html/body/div[2]/div[3]/ul')).at(0)?.click(), + page.waitForNavigation() + ]) + }) + + then('is redirected to the sign in page', async () => { + await expect(page).toMatch('WELCOME') + }) + }) + + afterAll(async () => { + browser.close() + }) +}) diff --git a/webapp/package.json b/webapp/package.json index 647fcf4..d84dfb4 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -32,7 +32,8 @@ "coveragePathIgnorePatterns": [ "src/reportWebVitals.ts", "src/index.tsx", - "src/components/Stepper/customisedComponents/*" + "src/components/Stepper/customisedComponents/*", + "src/components/Orders/Orders.tsx" ]}, "scripts": { "start": "react-scripts start", diff --git a/webapp/src/components/Cart/CartItem.test.tsx b/webapp/src/components/Cart/CartItem.test.tsx index 856d731..bdd51cb 100644 --- a/webapp/src/components/Cart/CartItem.test.tsx +++ b/webapp/src/components/Cart/CartItem.test.tsx @@ -1,20 +1,21 @@ -import { render, screen } from '@testing-library/react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import { CartProvider } from '../../contexts/CartContext'; import { CartProduct } from '../../shared/shareddtypes' import CartItem from './CartItem' -test('check order renders propertly', async () => { - // Arrange - const product: CartProduct = { - _id: '1234', - name: 'Blue', - price: 5, - size: '40', - quantity: 45, - image: '' - } +// Arrange +const product: CartProduct = { + _id: '1234', + name: 'Blue', + price: 5, + size: '40', + quantity: 45, + image: '' +} +test('check cart item renders propertly', async () => { // Act - render() + render() // Assert expect(screen.getByText(product.name)).toBeInTheDocument() @@ -22,3 +23,57 @@ test('check order renders propertly', async () => { expect(screen.getByText(product.price + " €")).toBeInTheDocument() expect(screen.getByText(product.quantity)).toBeInTheDocument() }) + +test('add a unit of product while inside the cart', async () => { + // Act + render() + + // add + let addButton = screen.getByRole('button', { + name: 'add' + }) + fireEvent.click(addButton); + + // Assert + expect(screen.getByText(product.name)).toBeInTheDocument() + expect(screen.getByText("Size " + product.size)).toBeInTheDocument() + expect(screen.getByText(product.price + " €")).toBeInTheDocument() + waitFor(() => { + expect(product.price + 1).toBeInTheDocument() + }) +}) + +test('remove a unit of product while inside the cart', async () => { + // Act + render() + + // remove + let removeButton = screen.getByRole('button', { + name: 'remove' + }) + fireEvent.click(removeButton); + + // Assert + expect(screen.getByText(product.name)).toBeInTheDocument() + expect(screen.getByText("Size " + product.size)).toBeInTheDocument() + expect(screen.getByText(product.price + " €")).toBeInTheDocument() + waitFor(() => { + expect(product.price - 1).toBeInTheDocument() + }) +}) + +test('remove all units of a product while inside the cart', async () => { + // Act + render() + + // remove all + let removeAllButton = screen.getByRole('button', { + name: 'removeAll' + }) + fireEvent.click(removeAllButton); + + // Assert + waitFor(() => { + expect(screen.getByText(product.name)).not.toBeInTheDocument() + }); +}) \ No newline at end of file diff --git a/webapp/src/components/Cart/CartItem.tsx b/webapp/src/components/Cart/CartItem.tsx index bfb07b7..a630467 100644 --- a/webapp/src/components/Cart/CartItem.tsx +++ b/webapp/src/components/Cart/CartItem.tsx @@ -72,13 +72,14 @@ export default function CartItem({ product }: Props) { Size {product.size} -