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: Cypress e2e setup #2552

Open
wants to merge 10 commits into
base: alpha
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,26 @@ jobs:
run: ./scripts/before_script.sh
env:
CI: true
check-ui:
name: UI Check
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Install dependencies
run: npm ci --ignore-scripts
- name: Test dashboard
uses: cypress-io/github-action@v5
timeout-minutes: 10
with:
start: npm run start
browser: chrome
wait-on: 'http://localhost:4040'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ test_logs

# visual studio code
.vscode

# cypress
cypress/screenshots
cypress/videos
10 changes: 8 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ When working on the dashboard, use `npm run dashboard` and visit `localhost:4040

When working on React components, use `npm run pig` and visit `localhost:4041` to view our component library and documentation (you can have both Dashboard and PIG running at once). The demos for each component are the primary way we test components, although there are also a small number of automated tests you can run with `npm test`. If you would like to create a new component that does not exist in the component library, use `npm run generate yourComponentName` to generate boilerplate code and quickly get started.

## UI Tests

1. Start the dashboard with `npm run dashboard`. Make sure that dashboard server is running on port `4040`.
2. Start the UI tests in interactive mode for debugging with `npm run cypress:open`, or run the tests silently with `npm run cypress:run`.

## Pull Requests
mtrezza marked this conversation as resolved.
Show resolved Hide resolved
We actively welcome your pull requests.

We welcome and appreciate your contribution to Parse Dashboard. Please keep the following in mind:

1. Fork the repo and create your branch from the `alpha` branch.
2. If you've added code that should be tested, add tests.
2. If you've modified code, add a test. If the modification involves UI changes, add a UI test.
3. If you've changed APIs, update the documentation.
4. If you've updated/added an UI component, please add a screenshot.
5. If you've fixed an issue or added features, add what you've changed to the CHANGELOG.
Expand Down
8 changes: 8 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { defineConfig } = require('cypress')

module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:4040',
video: true,
},
})
8 changes: 8 additions & 0 deletions cypress/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": [
"cypress"
],
"env": {
"cypress/globals": true
}
}
145 changes: 145 additions & 0 deletions cypress/e2e/apps-index.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { getDashboardConfig } from '../utils/getDashboardConfig';

describe('Apps index', () => {
it('Redirects to app lists from root path if there are 2 or more apps', () => {
cy.intercept(
{
method: 'GET',
url: '/parse-dashboard-config.json',
},
getDashboardConfig([
{
appName: 'test1',
},
{
appName: 'test2',
},
]),
);

cy.visit('/')
cy.wait(3000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it really necessary to wait here? Doesn't cypress handle the loading and continue once the page loading finished?


cy
.url()
.should('match', /\/apps$/);
});

it('Can filter apps with search input', () => {
cy.intercept(
{
method: 'GET',
url: '/parse-dashboard-config.json',
},
getDashboardConfig([
{
appName: 'test1',
},
{
appName: 'test2',
},
]),
);

cy.visit('/apps')
cy.wait(3000)

cy
.get('[data-cy="apps-index-app"]')
.should('have.length', 2)

cy
.get('[data-cy="apps-index-search-input"]')
.type('test1')

cy
.get('[data-cy="apps-index-app"]')
.should('have.length', 1)

cy
.get('[data-cy="apps-index-search-input"]')
.type('qwerty')

cy
.get('[data-cy="apps-index-app"]')
.should('have.length', 0)
})

it('Can show error states in the list', () => {
cy.intercept(
{
method: 'GET',
url: '/parse-dashboard-config.json',
},
getDashboardConfig([
{
appName: 'test1',
},
{
appName: 'test2',
masterKey: 'cos',
},
{
appName: 'test3',
appId: 'cos',
},
{
appName: 'test4',
masterKey: 'cos',
appId: 'cos',
},
{
appName: 'test5',
serverURL: 'http://unavailablehost:1234'
},
{
appName: 'test6',
serverURL: 'http://localhost:1337/parse2'
},
]),
);

cy.visit('/apps')
cy.wait(3000)

cy
.get('[data-cy="apps-index-app"]')
.should('have.length', 6)

cy
.get('[data-cy="apps-index-app"]')
.eq(0)
.find('[data-cy="apps-index-app-details"]')
.contains('Server URL: http://localhost:1337/parse')

cy
.get('[data-cy="apps-index-app"]')
.eq(1)
.find('[data-cy="apps-index-app-details"]')
.contains('Server not reachable: unauthorized')

cy
.get('[data-cy="apps-index-app"]')
.eq(2)
.find('[data-cy="apps-index-app-details"]')
.contains('Server not reachable: unauthorized')

cy
.get('[data-cy="apps-index-app"]')
.eq(3)
.find('[data-cy="apps-index-app-details"]')
.contains('Server not reachable: unauthorized')

cy
.get('[data-cy="apps-index-app"]')
.eq(4)
.find('[data-cy="apps-index-app-details"]')
.contains('Server not reachable: unable to connect to server')

cy
.get('[data-cy="apps-index-app"]')
.eq(5)
.find('[data-cy="apps-index-app-details"]')
.contains('Server not reachable: unable to connect to server')
});
})
30 changes: 30 additions & 0 deletions cypress/e2e/dashboard.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

import { getDashboardConfig } from '../utils/getDashboardConfig';

describe('Dashboard', () => {
it('Shows loader during loading', () => {
cy.intercept(
{
method: 'GET',
url: '/parse-dashboard-config.json',
},
(req) => {
req.reply({
statusCode: 200,
delay: 250,
body: getDashboardConfig([
{
appName: 'test',
}
]),
})
}
)

cy.visit('/')

cy.get('[data-cy="dashboard-loader"]', {
timeout: 250
});
})
})
5 changes: 5 additions & 0 deletions cypress/fixtures/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "[email protected]",
"body": "Fixtures are a great way to mock data for responses to routes"
}
mehulmathur16 marked this conversation as resolved.
Show resolved Hide resolved
37 changes: 37 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// <reference types="cypress" />
mehulmathur16 marked this conversation as resolved.
Show resolved Hide resolved
// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
20 changes: 20 additions & 0 deletions cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
mehulmathur16 marked this conversation as resolved.
Show resolved Hide resolved
// 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')
14 changes: 14 additions & 0 deletions cypress/utils/getDashboardConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function getDashboardConfig(apps) {
return {
newFeaturesInLatestVersion: [],
apps: apps.map(app => ({
serverURL: app.serverURL || 'http://localhost:1337/parse',
appId: app.appId || 'hello',
masterKey: app.masterKey || 'world',
appName: app.appName,
iconName: app.iconName || '',
primaryBackgroundColor: app.primaryBackgroundColor || '',
secondaryBackgroundColor: app.secondaryBackgroundColor || '',
})),
};
}
Loading
Loading