Skip to content

Commit 0952ca5

Browse files
authored
Cypress e2e (#95)
e2e tests
1 parent ac65e8c commit 0952ca5

25 files changed

+2401
-117
lines changed
File renamed without changes.

.env.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VITE_CB_ENDPOINT=http://localhost:5000

.github/workflows/build_frontend.yml

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,45 @@ name: build CoderBot frontend
66
on: push
77

88
jobs:
9-
build-frontend:
9+
test:
1010
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v3
14+
- name: Docker login
15+
run: docker login ghcr.io -u previ -p ${{ secrets.GHCR_BACKEND_STUB_RO }}
16+
- name: Run stub
17+
run: docker run -dp 5000:5000 --name backend-stub ghcr.io/coderbotorg/backend:stub-latest
18+
# Install NPM dependencies, cache them correctly
19+
# and run all Cypress tests
20+
- name: Cypress run
21+
uses: cypress-io/github-action@v4
22+
with:
23+
build: npm run build-test
24+
start: npm run preview
25+
wait-on: 'http://localhost:5000/api/v1/openapi.json'
26+
wait-on-timeout: 60
27+
browser: chrome
28+
record: true
29+
parallel: true
30+
group: 'UI - Chrome'
31+
env:
32+
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
33+
# Recommended: pass the GitHub token lets this action correctly
34+
# determine the unique run id necessary to re-run the checks
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
- name: Clean up
37+
run: docker stop backend-stub && docker rm backend-stub
1138

12-
strategy:
13-
matrix:
14-
node-version: [16.x]
15-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
16-
39+
build:
40+
runs-on: ubuntu-latest
41+
needs: test
1742
steps:
1843
- uses: actions/checkout@v3
19-
- name: Use Node.js ${{ matrix.node-version }}
44+
- name: Use Node.js 16.x
2045
uses: actions/setup-node@v3
2146
with:
22-
node-version: ${{ matrix.node-version }}
47+
node-version: 16
2348
cache: 'npm'
2449
- run: npm ci
2550
- run: npm run build --if-present
@@ -29,9 +54,9 @@ jobs:
2954
name: dist
3055
path: dist
3156

32-
release-frontend:
57+
release:
3358
runs-on: ubuntu-latest
34-
needs: build-frontend
59+
needs: build
3560
steps:
3661
- uses: actions/checkout@v3 # Checking out the repo
3762
- name: Docker meta

cypress.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const { defineConfig } = require("cypress");
2+
3+
module.exports = defineConfig({
4+
projectId: 'm2n47r',
5+
e2e: {
6+
experimentalStudio: true,
7+
setupNodeEvents(on, config) {
8+
// implement node event listeners here
9+
},
10+
},
11+
});

cypress/e2e/100_home.cy.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
describe('load homepage', () => {
2+
it('passes', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('.logo').should('have.text', 'CoderBot')
6+
cy.get('a[href*="program"]').should('exist')
7+
cy.get('a[href*="control"]').should('exist')
8+
cy.get('a[href*="settings"]').should('exist')
9+
})
10+
11+
it('checks sidebar contents', () => {
12+
cy.visit('http://localhost:8080')
13+
cy.get('.v-carousel').should('exist')
14+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
15+
cy.get('a[href="#/"]').should('exist')
16+
cy.get('a[href*="/docs"]').should('exist')
17+
cy.get('a[href*="/activity/manage"]').should('exist')
18+
cy.get('a[href*="/program"]').should('exist')
19+
cy.get('a[href*="/control"]').should('exist')
20+
cy.get('a[href*="/gallery"]').should('exist')
21+
cy.get('a[href*="/settings"]').should('exist')
22+
})
23+
})

cypress/e2e/200_docs.cy.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
describe('load homepage', () => {
2+
it('checks sidebar contents', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
6+
cy.get('a.v-list-item[href*="/docs"]').should('exist')
7+
cy.get('a.v-list-item[href*="/docs"]').invoke('removeAttr', 'target').click()
8+
cy.url().should('include', '/docs')
9+
})
10+
})

cypress/e2e/300_activities.cy.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
describe('activities page', () => {
2+
it('loads the default activity', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
6+
cy.get('a.v-list-item[href*="activity/manage"]').click()
7+
cy.get('#activity_list').should('exist')
8+
cy.get('div#default').should('exist')
9+
cy.get('div#default i.mdi-star').should('exist')
10+
cy.get('div#default .v-list-item-title').should('have.text', 'default')
11+
cy.get('div#default a[href*="activity/edit/default"]').should('exist')
12+
cy.get('#app').click()
13+
cy.get('div#default a[href*="activity/edit/default"]').click()
14+
})
15+
16+
it('creates a new activity', () => {
17+
cy.visit('http://localhost:8080')
18+
cy.get('.v-carousel').should('exist')
19+
cy.get('button.v-app-bar-nav-icon').should('exist')
20+
cy.get('button.v-app-bar-nav-icon').click()
21+
cy.get('a.v-list-item[href*="activity/manage"]').click()
22+
cy.get('#app').click()
23+
cy.get('a.v-btn[href*="activity/new"]').should('exist')
24+
cy.get('a.v-btn[href*="activity/new"]').click()
25+
cy.get('input#name').should('exist')
26+
cy.get('input#desc').should('exist')
27+
cy.get('input#name').type("test-activity-name")
28+
cy.get('input#desc').type("Test activity description")
29+
cy.get('button#toolbox').should('exist')
30+
cy.get('button#toolbox').click()
31+
cy.get('button#add_category').should('exist')
32+
cy.get('button#add_category_all').should('exist')
33+
cy.get('button#add_category_all').click()
34+
cy.get('button#save').should('exist')
35+
cy.get('button#save').click()
36+
/*
37+
cy.get('input#input-317').should('exist')
38+
cy.get('input#input-317').type("Test Category")
39+
cy.get('button#category_ok').should('exist')
40+
cy.get('button#category_ok').click()
41+
*/
42+
})
43+
it('deletes an existing activity', () => {
44+
cy.visit('http://localhost:8080')
45+
cy.get('.v-carousel').should('exist')
46+
cy.get('button.v-app-bar-nav-icon').should('exist')
47+
cy.get('button.v-app-bar-nav-icon').click()
48+
cy.get('a[href*="activity/manage"]').click()
49+
cy.get('#app').click()
50+
cy.get('div#test-activity-name').find('i.mdi-delete').should("exist")
51+
cy.get('div#test-activity-name').find('i.mdi-delete').click()
52+
cy.get('button#confirmDeleteDlg_ok').should("exist")
53+
cy.get('button#confirmDeleteDlg_ok').click()
54+
})
55+
})

cypress/e2e/400_program.cy.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
describe('load homepage', () => {
2+
it('checks toolbar bar contents', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
6+
cy.get('a.v-list-item[href*="/program"]').should('exist')
7+
cy.get('a.v-list-item[href*="/program"]').click()
8+
cy.get('#app').click()
9+
cy.get('button#clearProgramDlg').should('exist')
10+
cy.get('button#saveProgram').should('exist')
11+
cy.get('button#toggleSaveAs').should('exist')
12+
cy.get('button#loadProgramList').should('exist')
13+
cy.get('button#runProgram').should('exist')
14+
cy.get('button#getProgramCode').should('exist')
15+
cy.get('button#exportProgram').should('exist')
16+
cy.get('button#pickFile').should('exist')
17+
})
18+
19+
it('checks blockly loades', () => {
20+
cy.visit('http://localhost:8080')
21+
cy.get('.v-carousel').should('exist')
22+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
23+
cy.get('a.v-list-item[href*="/program"]').should('exist')
24+
cy.get('a.v-list-item[href*="/program"]').click()
25+
cy.get('#app').click()
26+
cy.get('.blocklyTotal').should('exist')
27+
cy.get('.blocklyToolboxDiv').should('exist')
28+
cy.get('.blocklyWorkspace').should('exist')
29+
})
30+
31+
it('loads an existing program', () => {
32+
cy.visit('http://localhost:8080')
33+
cy.get('.v-carousel').should('exist')
34+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
35+
cy.get('a.v-list-item[href*="/program"]').should('exist')
36+
cy.get('a.v-list-item[href*="/program"]').click()
37+
cy.get('#app').click()
38+
cy.get('button#loadProgramList').should('exist')
39+
cy.get('button#loadProgramList').click()
40+
cy.contains('test_sonars').should('exist')
41+
cy.contains('test_sonars').click()
42+
cy.get('.blocklyPath').should('exist')
43+
cy.get('button#clearProgramDlg').click()
44+
cy.get('button.text-green').should('exist')
45+
cy.get('button.text-green').click()
46+
})
47+
})

cypress/e2e/500_control.cy.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
describe('load homepage', () => {
2+
it('checks sidebar contents', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
6+
cy.get('a.v-list-item[href*="control"]').click()
7+
cy.get('img[src*="video/stream"]').should('exist')
8+
})
9+
})

cypress/e2e/600_gallery.cy.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
describe('load homepage', () => {
2+
it('checks sidebar contents', () => {
3+
cy.visit('http://localhost:8080')
4+
cy.get('.v-carousel').should('exist')
5+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
6+
cy.get('a.v-list-item[href*="/gallery"]').should('exist')
7+
cy.get('a.v-list-item[href*="/gallery"]').click()
8+
cy.get('h3').should('exist')
9+
})
10+
})

cypress/e2e/700_settings.cy.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
describe('load homepage', () => {
2+
it('checks tabs', () => {
3+
cy.intercept('http://localhost:5000/wifi/v1/connection_status', (req) => {
4+
req.reply({"wifi": false, "internet": true})
5+
})
6+
cy.intercept('http://localhost:5000/wifi/v1/list_access_points', (req) => {
7+
req.reply({"ssids": [{"ssid": "wifi-home", "conn_type": "WPA2", "strength": 99}]})
8+
})
9+
cy.visit('http://localhost:8080')
10+
cy.get('.v-carousel').should('exist')
11+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
12+
cy.get('a.v-list-item[href*="/settings"]').click()
13+
cy.get('#app').click()
14+
cy.get('button#0').should('exist')
15+
cy.get('button#1').should('exist')
16+
cy.get('button#2').should('exist')
17+
cy.get('button#3').should('exist')
18+
cy.get('button#4').should('exist')
19+
cy.get('button#5').should('exist')
20+
cy.get('button#6').should('exist')
21+
cy.get('button#7').should('exist')
22+
cy.get('button#8').should('exist')
23+
})
24+
25+
it('checks password', () => {
26+
cy.intercept('http://localhost:5000/wifi/v1/connection_status', (req) => {
27+
req.reply({"wifi": false, "internet": true})
28+
})
29+
cy.intercept('http://localhost:5000/wifi/v1/list_access_points', (req) => {
30+
req.reply({"ssids": [{"ssid": "wifi-home", "conn_type": "WPA2", "strength": 99}]})
31+
})
32+
cy.visit('http://localhost:8080')
33+
cy.get('.v-carousel').should('exist')
34+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
35+
cy.get('a.v-list-item[href*="/settings"]').click()
36+
cy.get('#app').click()
37+
cy.get('button#0').should('exist')
38+
cy.get('button#0').click()
39+
cy.get('#settings_password').type('secret')
40+
cy.get('#save').should('exist')
41+
cy.get('#save').click()
42+
cy.visit('http://localhost:8080')
43+
cy.get('.v-carousel').should('exist')
44+
cy.get('button.v-app-bar-nav-icon').should('exist').click()
45+
cy.get('a.v-list-item[href*="/settings"]').click()
46+
cy.get('#settings_password_verify').should('exist')
47+
cy.get('#settings_password_verify').type('secret')
48+
cy.get('#settings_password_verify_ok').click()
49+
cy.get('button#0').should('exist')
50+
})
51+
})

cypress/fixtures/example.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "[email protected]",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}

cypress/support/commands.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ***********************************************
2+
// This example commands.js shows you how to
3+
// create various custom commands and overwrite
4+
// existing commands.
5+
//
6+
// For more comprehensive examples of custom
7+
// commands please read more here:
8+
// https://on.cypress.io/custom-commands
9+
// ***********************************************
10+
//
11+
//
12+
// -- This is a parent command --
13+
// Cypress.Commands.add('login', (email, password) => { ... })
14+
//
15+
//
16+
// -- This is a child command --
17+
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18+
//
19+
//
20+
// -- This is a dual command --
21+
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22+
//
23+
//
24+
// -- This will overwrite an existing command --
25+
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

cypress/support/e2e.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// ***********************************************************
2+
// This example support/e2e.js is processed and
3+
// loaded automatically before your test files.
4+
//
5+
// This is a great place to put global configuration and
6+
// behavior that modifies Cypress.
7+
//
8+
// You can change the location of this file or turn off
9+
// automatically serving support files with the
10+
// 'supportFile' configuration option.
11+
//
12+
// You can read more here:
13+
// https://on.cypress.io/configuration
14+
// ***********************************************************
15+
16+
// Import commands.js using ES2015 syntax:
17+
import './commands'
18+
19+
// Alternatively you can use CommonJS syntax:
20+
// require('./commands')

0 commit comments

Comments
 (0)