Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: setup E2E tests with Cypress #572

Merged
merged 5 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: End-to-end tests

on: push

jobs:
cypress-run:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
# Install npm dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v6
with:
start: yarn dev
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ We use the [yarn](https://yarnpkg.com/getting-started/install) for package manag
```sh
yarn build
```

## Tests

```sh
yarn test
```
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Works with an Open Prices server, see https://github.com/openfoodfacts/open-pric

## Contribute

See [CONTRIBUTING.md](https://github.com/openfoodfacts/open-prices-frontend/blob/main/CONTRIBUTING.md)
See [CONTRIBUTING.md](https://github.com/openfoodfacts/open-prices-frontend/blob/master/CONTRIBUTING.md)

<details><summary><h2>Weekly meetings</h2></summary>
* see https://github.com/openfoodfacts/open-prices#weekly-meetings
Expand Down
16 changes: 16 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
e2e: {
specPattern: '**/*.cy.js',
fixturesFolder: 'tests/fixtures',
screenshotsFolder: 'tests/screenshots',
videosFolder: 'tests/videos',
downloadsFolder: 'tests/downloads',
supportFile: 'tests/support/e2e.js',
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: 'http://localhost:5173',
},
});
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
"build": "vite build",
"build-staging": "vite build --base=/app/ --mode preprod",
"build-prod": "vite build --base=/app/ --mode prod",
"preview": "vite preview"
"preview": "vite preview",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test": "start-server-and-test dev http://localhost:5173 cy:run"
},
"dependencies": {
"@intlify/unplugin-vue-i18n": "^2.0.0",
Expand All @@ -33,7 +36,9 @@
"@vue/compiler-sfc": "^3.3.9",
"autoprefixer": "^10.4.16",
"cross-env": "^7.0.3",
"cypress": "^13.8.1",
"postcss": "^8.4.31",
"start-server-and-test": "^2.0.3",
"vite": "^4.5.0"
},
"resolutions": {
Expand Down
2 changes: 1 addition & 1 deletion src/components/PriceCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-card :id="'price_' + price.id">
<v-card :id="'price_' + price.id" data-name="price-card">
<v-container class="pa-2">
<v-row>
<v-col v-if="!hideProductImage" style="max-width:15%">
Expand Down
2 changes: 1 addition & 1 deletion src/components/PriceCountChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<v-chip label size="small" density="comfortable" :color="getColor()" class="mr-1">
<v-icon start icon="mdi-tag-outline"></v-icon>
<span v-if="withLabel">{{ $t('PriceCountChip.PriceCount', { count: count }) }}</span>
<span v-if="!withLabel">{{ count }}</span>
<span v-else id="price-count">{{ count }}</span>
</v-chip>
</template>

Expand Down
4 changes: 2 additions & 2 deletions src/components/ProductCard.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<v-card>
<v-card data-name="product-card">
<v-container class="pa-2">
<v-row>
<v-col style="max-width:25%">
<v-img v-if="product.image_url" :src="product.image_url" style="max-height:100px;width:100px" @click="goToProduct()"></v-img>
<v-img v-if="!product.image_url" :src="productImageDefault" style="height:100px;width:100px;filter:invert(.9);"></v-img>
</v-col>
<v-col style="max-width:75%">
<h3 @click="goToProduct()">{{ getProductTitle() }}</h3>
<h3 id="product-title" @click="goToProduct()">{{ getProductTitle() }}</h3>

<p>
<span>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProductMissingChip.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-chip label size="small" density="comfortable" prepend-icon="mdi-alert" color="error">
<v-chip label size="small" density="comfortable" prepend-icon="mdi-alert" color="error" data-name="product-missing-chip">
{{ $t('PriceCard.UnknownProduct') }}
</v-chip>
</template>
4 changes: 2 additions & 2 deletions src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<template v-slot:subtitle v-if="!loading">
<i18n-t keypath="Home.TodayPriceStat" :plural="todayPriceCount" tag="span">
<template v-slot:todayPriceNumber>
<span>{{ todayPriceCount }}</span>
<span id="price-count">{{ todayPriceCount }}</span>
</template>
</i18n-t>
</template>
Expand Down Expand Up @@ -95,7 +95,7 @@ export default {
methods: {
getTodayPriceCount() {
this.loading = true
return api.getPrices({ created__gte: utils.currentStartOfDay(), size: 2 })
return api.getPrices({ created__gte: utils.currentStartOfDay(), size: 1 })
.then((data) => {
this.todayPriceCount = data.total
this.loading = false
Expand Down
51 changes: 51 additions & 0 deletions tests/e2e/spec.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe('Basic tests', () => {
beforeEach(() => {
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/products?page=1&size=10&order_by=-price_count', { fixture: 'products.json' })
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/products/code/3011360030498', { fixture: 'product_3011360030498.json' })
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/prices?page=1&size=1&order_by=-created*', { fixture: 'prices.json' })
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/prices?page=1&size=10&order_by=-created', { fixture: 'prices.json' })
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/prices?page=1&size=10&order_by=-date&product_code=3011360030498', { fixture: 'product_3011360030498_prices.json' })
cy.intercept('GET', 'http://127.0.0.1:8000/api/v1/prices?page=1&size=10&order_by=-date&category_tag=en%3Apitted-apricot', { fixture: 'pitted_apricot_prices.json' })
})

it('loads the home page', () => {
cy.visit('/')
cy.contains('Welcome to Open Prices!') // en by default
cy.get('#price-count').contains('42')
})

it('displays the latest prices', () => {
cy.visit('/prices')
cy.contains('Welcome to Open Prices!').should('not.exist')
cy.get('[data-name="price-card"]').should('have.length', 10)
cy.contains('3564700428023') // unknown product
cy.get('[data-name="product-missing-chip"]').should('have.length', 1)
})

it('displays the top products', () => {
cy.visit('/products')
cy.contains('Welcome to Open Prices!').should('not.exist')
cy.get('[data-name="product-card"]').should('have.length', 10)
cy.contains('3973467869701') // unknown product
cy.contains('3250391696949') // unknown product
cy.get('[data-name="product-missing-chip"]').should('have.length', 2)
})

it('displays a product page', () => {
cy.visit('/products/3011360030498')
cy.contains('Welcome to Open Prices!').should('not.exist')
cy.get('#product-title').contains('Mayonnaise Classique')
cy.get('#price-count').contains('1')
cy.get('[data-name="product-missing-chip"]').should('have.length', 0)
cy.get('[data-name="price-card"]').should('have.length', 1)
})

it('displays a raw product page', () => {
cy.visit('/products/en:pitted-apricot')
cy.contains('Welcome to Open Prices!').should('not.exist')
cy.get('.v-card-title').contains('Pitted apricot')
// cy.get('#price-count').contains('1')
cy.get('[data-name="product-missing-chip"]').should('have.length', 2) // TODO: fix
cy.get('[data-name="price-card"]').should('have.length', 2)
})
})
104 changes: 104 additions & 0 deletions tests/fixtures/pitted_apricot_prices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"items": [
{
"product_code": null,
"product_name": null,
"category_tag": "en:pitted-apricot",
"labels_tags": null,
"origins_tags": [
"en:unknown"
],
"price": 4.0,
"price_is_discounted": false,
"price_without_discount": null,
"price_per": "UNIT",
"currency": "AED",
"location_osm_id": 32520537,
"location_osm_type": "WAY",
"date": "2024-03-31",
"proof_id": 224,
"id": 760,
"product_id": null,
"location_id": 32,
"owner": "user1",
"created": "2024-03-31T22:10:53.037731+02:00",
"product": null,
"proof": {
"id": 224,
"file_path": "0001/TWZOnskQhz.webp",
"mimetype": "image/webp",
"type": "RECEIPT",
"owner": "user1",
"price_count": 9,
"created": "2024-03-20T01:40:46.098715+01:00"
},
"location": {
"osm_id": 32520537,
"osm_type": "WAY",
"price_count": 16,
"id": 32,
"osm_name": "Monoprix",
"osm_display_name": "Monoprix, Rue Lafayette, Hyper Centre, Secteur 2, Grenoble, Isère, Auvergne-Rhône-Alpes, France métropolitaine, 38000, France",
"osm_address_postcode": "38000",
"osm_address_city": "Grenoble",
"osm_address_country": "France",
"osm_lat": 45.1904063,
"osm_lon": 5.7286464,
"created": "2024-02-09T17:43:00.113125+01:00",
"updated": "2024-03-31T22:13:49.569736+02:00"
}
},
{
"product_code": null,
"product_name": null,
"category_tag": "en:pitted-apricot",
"labels_tags": null,
"origins_tags": [
"en:germany"
],
"price": 3.0,
"price_is_discounted": false,
"price_without_discount": null,
"price_per": "KILOGRAM",
"currency": "AED",
"location_osm_id": 32520537,
"location_osm_type": "WAY",
"date": "2024-03-20",
"proof_id": 224,
"id": 759,
"product_id": null,
"location_id": 32,
"owner": "user1",
"created": "2024-03-20T14:27:21.575599+01:00",
"product": null,
"proof": {
"id": 224,
"file_path": "0001/TWZOnskQhz.webp",
"mimetype": "image/webp",
"type": "RECEIPT",
"owner": "user1",
"price_count": 9,
"created": "2024-03-20T01:40:46.098715+01:00"
},
"location": {
"osm_id": 32520537,
"osm_type": "WAY",
"price_count": 16,
"id": 32,
"osm_name": "Monoprix",
"osm_display_name": "Monoprix, Rue Lafayette, Hyper Centre, Secteur 2, Grenoble, Isère, Auvergne-Rhône-Alpes, France métropolitaine, 38000, France",
"osm_address_postcode": "38000",
"osm_address_city": "Grenoble",
"osm_address_country": "France",
"osm_lat": 45.1904063,
"osm_lon": 5.7286464,
"created": "2024-02-09T17:43:00.113125+01:00",
"updated": "2024-03-31T22:13:49.569736+02:00"
}
}
],
"total": 2,
"page": 1,
"size": 10,
"pages": 1
}
Loading
Loading