Skip to content

Commit

Permalink
Cypress E2E (#1507)
Browse files Browse the repository at this point in the history
* add cypress specs and workflow
  • Loading branch information
lastminutediorama authored May 20, 2024
1 parent c38f836 commit 324599a
Show file tree
Hide file tree
Showing 15 changed files with 2,234 additions and 191 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/cypress-manual.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Cypress Full Tests

on: workflow_dispatch

env:
CYPRESS_TEST_BASE_URL: ${{ vars.CYPRESS_TEST_BASE_URL }}
CYPRESS_TEST_USER1: ${{ secrets.CYPRESS_TEST_USER1 }}
CYPRESS_TEST_PASS1: ${{ secrets.CYPRESS_TEST_PASS1 }}

jobs:
cypress-run:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install dependencies
run: npm ci

- name: Cypress run
uses: cypress-io/github-action@v6
with:
working-directory: src/interface # runs every cypress test under this dir
record: false
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ node_modules/
# pycharm
.idea/

# Cypress artifacts
src/interface/cypress/screenshots/


pyrightconfig.json

Session.vim

3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ deploy-frontend: install-dependencies-frontend compile-angular
deploy-storybook: install-dependencies-frontend build-storybook
cp -r ./src/interface/storybook-static/** ${STORYBOOK_WWW_DIR}

cypress-test:
cd src/interface && npm run cypress:run

migrate:
cd src/planscape && python3 manage.py migrate --no-input

Expand Down
15 changes: 15 additions & 0 deletions src/interface/cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineConfig } from 'cypress';

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:4200',
},

component: {
devServer: {
framework: 'angular',
bundler: 'webpack',
},
specPattern: '**/*.cy.ts',
},
});
53 changes: 53 additions & 0 deletions src/interface/cypress/e2e/accounts.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { randString, urls, baseURL, testPass, testUser } from '../support/util';

const newAcctEmail = 'cypress+' + randString(6) + '@local.example';
const newAcctPass = randString(15);
const newAcctFirstName = 'Cypress';
const newAcctLastName = 'Cypressington';

describe('Create an account with unmatching password', () => {
it('Attempts to create an account with bad password', () => {
cy.envCheck();
cy.visit(urls.SIGNUP);
cy.contains('Create your account');
cy.get('[formControlName="firstName"]').type(newAcctFirstName);
cy.get('[formControlName="lastName"]').type(newAcctLastName);
cy.get('[formControlName="email"]').type(newAcctEmail);
cy.get('.signup-title').click();
cy.get('[formControlName="password1"]').type(newAcctPass);
cy.get('.signup-title').click();
cy.get('[formControlName="password2"]').type('Nope!#%');
cy.get('.signup-title').click();
cy.contains('Given passwords must match.');
cy.get('button[type="submit"]').should('be.disabled');
});

it('Attempts to create an account', () => {
cy.envCheck();
cy.visit(urls.SIGNUP);
cy.contains('Create your account');
cy.get('[formControlName="firstName"]').type(newAcctFirstName);
cy.get('[formControlName="lastName"]').type(newAcctLastName);
cy.get('[formControlName="email"]').type(newAcctEmail);
cy.get('[formControlName="password1"]').type(newAcctPass);
cy.get('.signup-title').click();
cy.get('[formControlName="password2"]').type(newAcctPass);
cy.get('.signup-title').click();
cy.get('button[type="submit"]').click();
cy.wait(2000).then(() => {
cy.contains('Thank You!');
});
});
});

describe('Login and logout with command', () => {
it('Logs in and logs out using command', () => {
cy.envCheck();
expect(testUser).to.exist;
expect(testPass).to.exist;
cy.doLogin(testUser, testPass);
cy.contains('Planning areas').should('be.visible');
cy.doLogout();
cy.contains('Welcome to Planscape').should('be.visible');
});
});
41 changes: 41 additions & 0 deletions src/interface/cypress/e2e/map.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { urls } from '../support/util';

describe('Map', () => {
it('Shows welcome page', () => {
cy.visit(urls.MAP);
cy.contains('Planning Areas');
});
it('Switches number of maps', () => {
cy.visit(urls.MAP);
cy.get('.map').should('be.visible');
cy.get('[aria-label="Show 1 map"]').click();
let maps = cy.get('.map:visible');
maps.should('have.length', 1);

cy.get('[aria-label="Show 2 maps"]').click();
maps = cy.get('.map:visible');
maps.should('have.length', 2);

cy.get('[aria-label="Show 4 maps"]').click();
maps = cy.get('.map:visible');
maps.should('have.length', 4);
});

it('should show the correct basemap map layer when changing basemap', () => {
cy.visit(urls.MAP);
cy.get('.mat-radio-button').contains('Road').click();
cy.get('img.leaflet-tile')
.invoke('attr', 'src')
.should('contain', 'tiles.stadiamaps.com/tiles/alidade_smooth/');

cy.get('.mat-radio-button').contains('Terrain').click();
cy.get('img.leaflet-tile')
.invoke('attr', 'src')
.should('contain', 'World_Terrain_Base/MapServer');

cy.get('.mat-radio-button').contains('Satellite').click();
cy.get('img.leaflet-tile')
.invoke('attr', 'src')
.should('contain', 'World_Imagery/MapServer');
});
});
12 changes: 12 additions & 0 deletions src/interface/cypress/e2e/plan.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { testPass, testUser } from '../support/util';
// TODO: use config'd user
describe('Draws a map', () => {
it('Logs in and draws a map', () => {
cy.doLogin(testUser, testPass);
cy.contains('Planning areas').should('be.visible');

// go to Explore
cy.get('a[href="/map"]').click();
cy.contains('Map Control Panel').should('be.visible');
});
});
12 changes: 12 additions & 0 deletions src/interface/cypress/e2e/scenario.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { testPass, testUser } from '../support/util';
// TODO: use config'd user
describe('Creates a scenario', () => {
it('Logs in and creates a new scenario', () => {
cy.doLogin(testUser, testPass);
cy.contains('Planning areas').should('be.visible');

// go to Explore
cy.get('a[href="/map"]').click();
cy.contains('Map Control Panel').should('be.visible');
});
});
33 changes: 33 additions & 0 deletions src/interface/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { urls, baseURL, testPass, testUser } from '../support/util';

declare namespace Cypress {
interface Chainable {
doLogin(user: string, pass: string): void;
doLogout(): void;
envCheck(): void;
}
}

Cypress.Commands.add('doLogin', (user: string, pass: string) => {
cy.visit(urls.LOGIN);
cy.get('[formControlName="email"]').type(user);
cy.get('[formControlName="password"]').type(pass);
cy.get('button[type="submit"]').click();
});

Cypress.Commands.add('doLogout', () => {
cy.get('button[data-id="menu-trigger"]').click();
cy.get('button[data-id="logout"]').click();
});

Cypress.Commands.add('envCheck', () => {
if (!baseURL) {
throw new Error('Base Url is not defined');
}
if (!testUser) {
throw new Error('testUser is not defined');
}
if (!testPass) {
throw new Error('Base Url is not defined');
}
});
20 changes: 20 additions & 0 deletions src/interface/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands';

// Alternatively you can use CommonJS syntax:
require('./commands');
18 changes: 18 additions & 0 deletions src/interface/cypress/support/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const baseURL =
Cypress.env('TEST_BASE_URL') || 'https://dev.planscape.org';
export const testUser = Cypress.env('TEST_USER1');
export const testPass = Cypress.env('TEST_PASS1');

export const urls = {
LOGIN: baseURL + '/login',
SIGNUP: baseURL + '/signup',
MAP: baseURL + '/map',
};

export function randString(strlen: number) {
let random_string = '';
for (let i = 0; i < strlen; i++) {
random_string += String.fromCharCode(Math.floor(Math.random() * 25 + 97));
}
return random_string;
}
8 changes: 8 additions & 0 deletions src/interface/cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"sourceMap": false,
"types": ["cypress"]
}
}
Loading

0 comments on commit 324599a

Please sign in to comment.