Skip to content

Instantly share code, notes, and snippets.

@chrisvfritz
Created January 3, 2020 17:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisvfritz/75a426d3e7545110801df2e9c92081e8 to your computer and use it in GitHub Desktop.
Save chrisvfritz/75a426d3e7545110801df2e9c92081e8 to your computer and use it in GitHub Desktop.
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 3f0d9ab..588b1f8 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -36,16 +36,11 @@ jobs:
- run:
name: Run tests
- # Install libgconf, needed by Cypress' Electron, then run tests
- command: sudo apt-get install libgconf-2-4 && yarn test:ci
+ command: yarn test:ci
# Store test artifacts
- store_artifacts:
path: tests/unit/coverage
- - store_artifacts:
- path: tests/e2e/videos
- - store_artifacts:
- path: tests/e2e/screenshots
build:
<<: *vm_settings
diff --git a/.eslintrc.js b/.eslintrc.js
index c92a928..308e19f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -50,7 +50,7 @@ module.exports = {
},
overrides: [
{
- files: ['src/**/*', 'tests/unit/**/*', 'tests/e2e/**/*'],
+ files: ['src/**/*', 'tests/unit/**/*'],
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module',
diff --git a/.gitignore b/.gitignore
index efc68ed..be0958e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,8 +7,6 @@ node_modules/
# Dev/Build Artifacts
/dist/
-/tests/e2e/videos/
-/tests/e2e/screenshots/
/tests/unit/coverage/
jsconfig.json
diff --git a/README.md b/README.md
index d1637a7..2e9fab2 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
- [**Thorough documentation**](#documentation): Written with the same care as Vue's core docs to quickly train new team members and consolidate knowledge.
- [**Guaranteed consistency**](docs/linting.md): Opinionated linting for Vue, JavaScript/JSON, SCSS, and Markdown, integrated into Visual Studio Code and run against staged files on pre-commit.
-- [**First-class tests**](docs/tests.md): Practice test-driven development with both unit and end-to-end tests. Unit tests with Jest live as first-class citizens alongside your source files, while Cypress provides reliable end-to-end tests in an intuitive GUI for development.
+- [**First-class tests**](docs/tests.md): Practice test-driven development with both unit and end-to-end tests. Unit tests with Jest live as first-class citizens alongside your source files.
- [**Speedy development**](docs/development.md): Between [configurable generators](docs/development.md#generators), [handy aliases](docs/development.md#aliases), and [global base components](docs/development.md#base-components), your productivity will skyrocket.
## Getting started
diff --git a/cypress.json b/cypress.json
deleted file mode 100644
index 470c720..0000000
--- a/cypress.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "pluginsFile": "tests/e2e/plugins/index.js"
-}
diff --git a/docs/development.md b/docs/development.md
index 4513763..19e8b14 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -41,10 +41,6 @@ yarn dev
# Launch the dev server and automatically open it in
# your default browser when ready
yarn dev --open
-
-# Launch the dev server with the Cypress client for
-# test-driven development in a friendly interface
-yarn dev:e2e
```
### Developing with the production API
@@ -54,9 +50,6 @@ By default, dev and tests filter requests through [the mock API](/docs/tests.md#
```bash
# To develop against a local backend server
API_BASE_URL=http://localhost:3000 yarn dev
-
-# To test and develop against a production server
-API_BASE_URL=https://example.io yarn dev:e2e
```
## Generators
@@ -78,9 +71,6 @@ yarn new module
# Generate a new utility function with adjacent unit test
yarn new util
-
-# Generate a new end-to-end test
-yarn new e2e
```
Update existing or create new generators in the `generators` folder, with help from the [Hygen docs](http://www.hygen.io/).
diff --git a/docs/tests.md b/docs/tests.md
index 6698e6c..65c6ef4 100644
--- a/docs/tests.md
+++ b/docs/tests.md
@@ -8,10 +8,6 @@
- [Unit test files](#unit-test-files)
- [Unit test helpers](#unit-test-helpers)
- [Unit test mocks](#unit-test-mocks)
- - [End-to-end tests with Cypress](#end-to-end-tests-with-cypress)
- - [Running end-to-end tests](#running-end-to-end-tests)
- - [Introduction to Cypress](#introduction-to-cypress)
- - [Accessibility-driven end-to-end tests](#accessibility-driven-end-to-end-tests)
- [The mock API](#the-mock-api)
- [Mock authentication](#mock-authentication)
- [Testing/developing against a real server](#testingdeveloping-against-a-real-server)
@@ -63,132 +59,6 @@ Jest offers many tools for mocks, including:
- [For a source file](https://facebook.github.io/jest/docs/en/manual-mocks.html#mocking-user-modules), add the mock to a `__mocks__` directory adjacent to the file.
- [For a dependency in `node_modules`](https://facebook.github.io/jest/docs/en/manual-mocks.html#mocking-node-modules), add the mock to `tests/unit/__mocks__`. You can see an example of this with the `axios` mock, which intercepts requests with relative URLs to either [our mock API](#the-mock-api) or a local/live API if the `API_BASE_URL` environment variable is set.
-## End-to-end tests with Cypress
-
-### Running end-to-end tests
-
-```bash
-# Run end to end tests
-yarn test:e2e
-
-# Run the dev server with the Cypress client
-yarn dev:e2e
-```
-
-### Introduction to Cypress
-
-Cypress offers many advantages over other test frameworks, including the abilities to:
-
-- Travel through time to dissect the source of a problem when a test fails
-- Automatically record video and screenshots of your tests
-- Easily test in a wide range of screen sizes
-
-And much more! I recommend checking out our Cypress tests in `tests/e2e/specs`, then reading through at least these sections of the excellent Cypress docs:
-
-- [Core Concepts](https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Cypress-Is-Simple)
-- [Best Practices](https://docs.cypress.io/guides/references/best-practices.html)
-
-Beyond that, also know that you can access our app in Cypress on the `window`. For example, to dispatch a Vuex action that sets up some state:
-
-```js
-cy.window().then((window) => {
- return window.__app__.$store.dispatch('someModule/someAction')
-})
-```
-
-### Accessibility-driven end-to-end tests
-
-Ideally, tests should only fail when either:
-
-- something is actually broken, or
-- the requirements have changed
-
-Unfortunately, there are _a lot_ of ways to get this wrong. For example, when creating a selector for a login link:
-
-```js
-cy.get('a')
-// Too general, as there could be many links
-
-cy.get('.login-link')
-// Tied to implementation detail of CSS
-
-cy.get('#login-link')
-// Tied to implementation detail of JS and prevents component reusability
-
-cy.contains('Log in')
-// Assumes the text only appears in one context
-```
-
-To create the right selector, think from the perspective of the user. What _exactly_ are they looking for? They're not looking for:
-
-```js
-cy.get('a')
-// Any link
-
-cy.get('.login-link')
-// An element with a specific class
-
-cy.get('#login-link')
-// An element with a specific id
-
-cy.contains('Log in')
-// Specific text anywhere on the page
-```
-
-But rather:
-
-```js
-cy.contains('a', 'Log in')
-// A link containing the text "Log in"
-```
-
-Note that we're targeting a **semantic element**, meaning that it tells the web browser (and users) something about the element's role within the page. Also note that we're trying to be **as general as possible**. We're not looking for the link in a specific place, like a navbar or sidebar (unless that's part of the requirements), and we're not overly specific with the content. The link may also contain other content, like an icon, but that won't break the test, because we only care that _some link_ contains the text "Log in" _somewhere_ inside it.
-
-Now, some will be thinking:
-
-> "But isn't this brittle? Wouldn't it be better to add another attribute to the link, like `data-testid="login-link`? Then we could target that attribute and even if the element or content changes, the test won't break."
-
-I would argue that if the link's semantic element or content changes so drastically that it's no longer an anchor and doesn't even contain the text "Log in" anymore, the requirements _have_ changed, so the test _should_ break. And from an accessibility perspective, the app might indeed be broken.
-
-For example, let's imagine you replaced "Log in" with an icon:
-
-```html
-<a href="/login">
- <span class="icon icon-login"></span>
-</a>
-```
-
-Now users browsing your page with a screen reader will have no way to find the login link. From their perspective, this is just a link with no content. You may be tempted to try to fix the test with something like:
-
-```js
-cy.get('a[href="/login"]')
-// A link going to "/login"
-```
-
-But when you're trying to find a login link as a user, you don't just inspect the destination of unlabeled links until you find one that looks like it's possibly a login page. That would be a very slow and painful experience!
-
-Instead, thinking from a user's perspective forces you to stay accessible, perhaps updating your generated HTML to:
-
-```html
-<a aria-label="Log in" href="/login">
- <span aria-hidden="true" class="icon icon-login"></span>
-</a>
-```
-
-Then the selector in your test can update as well:
-
-```js
-cy.get('a[aria-label*="Log in"]')
-// A link with a label containing the text "Log in"
-```
-
-And the app now works for everyone:
-
-- Sighted users will see an icon that they'll (hopefully) have the cultural context to interpret as "Log in".
-- Non-sighted users get a label with the text "Log in" read to them.
-
-This strategy could be called **accessibility-driven end-to-end tests**, because you're parsing your own app with the same mindset as your users. It happens to be great for accessibility, but also helps to ensure that your app always breaks when requirements change, but never when you've just changed the implementation.
-
## The mock API
Working against the production API can be useful sometimes, but it also has some disadvantages:
diff --git a/generators/new/e2e/e2e.ejs.t b/generators/new/e2e/e2e.ejs.t
deleted file mode 100644
index 4f148b0..0000000
--- a/generators/new/e2e/e2e.ejs.t
+++ /dev/null
@@ -1,6 +0,0 @@
----
-to: tests/e2e/specs/<%= h.changeCase.kebab(name) %>.e2e.js
----
-describe('<%= h.changeCase.pascal(name) %>', () => {
-
-})
diff --git a/generators/new/e2e/prompt.js b/generators/new/e2e/prompt.js
deleted file mode 100644
index ff2b072..0000000
--- a/generators/new/e2e/prompt.js
+++ /dev/null
@@ -1,13 +0,0 @@
-module.exports = [
- {
- type: 'input',
- name: 'name',
- message: 'Name:',
- validate(value) {
- if (!value.length) {
- return 'Components must have a name.'
- }
- return true
- },
- },
-]
diff --git a/package.json b/package.json
index 5d7df95..9b12da8 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,6 @@
"private": true,
"scripts": {
"dev": "vue-cli-service serve",
- "dev:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --mode=development",
"build": "vue-cli-service build --modern",
"build:ci": "yarn build --report",
"lint:eslint": "eslint --fix",
@@ -20,9 +19,8 @@
"test:unit:file": "yarn test:unit --bail --findRelatedTests",
"test:unit:watch": "yarn test:unit --watch --notify --notifyMode change",
"test:unit:ci": "yarn test:unit --coverage --ci",
- "test:e2e": "cross-env VUE_APP_TEST=e2e vue-cli-service test:e2e --headless",
- "test": "run-s test:unit test:e2e",
- "test:ci": "run-s test:unit:ci test:e2e",
+ "test": "run-s test:unit",
+ "test:ci": "run-s test:unit:ci",
"new": "cross-env HYGEN_TMPLS=generators hygen new",
"docs": "vuepress dev",
"docker": "docker-compose exec dev yarn"
@@ -47,7 +45,6 @@
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.1.2",
- "@vue/cli-plugin-e2e-cypress": "4.1.2",
"@vue/cli-plugin-eslint": "4.1.2",
"@vue/cli-plugin-unit-jest": "4.1.2",
"@vue/cli-service": "4.1.2",
diff --git a/src/main.js b/src/main.js
index a82005e..b4727c1 100644
--- a/src/main.js
+++ b/src/main.js
@@ -7,22 +7,8 @@ import '@components/_globals'
// Don't warn about using the dev version of Vue in development.
Vue.config.productionTip = process.env.NODE_ENV === 'production'
-// If running inside Cypress...
-if (process.env.VUE_APP_TEST === 'e2e') {
- // Ensure tests fail when Vue emits an error.
- Vue.config.errorHandler = window.Cypress.cy.onUncaughtException
-}
-
-const app = new Vue({
+new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')
-
-// If running e2e tests...
-if (process.env.VUE_APP_TEST === 'e2e') {
- // Attach the app to the window, which can be useful
- // for manually setting state in Cypress commands
- // such as `cy.logIn()`.
- window.__app__ = app
-}
diff --git a/tests/e2e/.eslintrc.js b/tests/e2e/.eslintrc.js
deleted file mode 100644
index 796566c..0000000
--- a/tests/e2e/.eslintrc.js
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
- plugins: ['cypress'],
- env: {
- 'cypress/globals': true,
- },
-}
diff --git a/tests/e2e/plugins/index.js b/tests/e2e/plugins/index.js
deleted file mode 100644
index bae9da3..0000000
--- a/tests/e2e/plugins/index.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// https://docs.cypress.io/guides/guides/plugins-guide.html
-module.exports = (on, config) => {
- // Dynamic configuration
- // https://docs.cypress.io/guides/references/configuration.html
- return Object.assign({}, config, {
- // ===
- // General
- // https://docs.cypress.io/guides/references/configuration.html#Global
- // ===
- watchForFileChanges: true,
- // ===
- // Environment variables
- // https://docs.cypress.io/guides/guides/environment-variables.html#Option-1-cypress-json
- // ===
- env: {
- CI: process.env.CI,
- },
- // ===
- // Viewport
- // https://docs.cypress.io/guides/references/configuration.html#Viewport
- // ===
- viewportWidth: 1280,
- viewportHeight: 720,
- // ===
- // Animations
- // https://docs.cypress.io/guides/references/configuration.html#Animations
- // ===
- waitForAnimations: true,
- animationDistanceThreshold: 4,
- // ===
- // Timeouts
- // https://docs.cypress.io/guides/references/configuration.html#Timeouts
- // ===
- defaultCommandTimeout: 4000,
- execTimeout: 60000,
- pageLoadTimeout: 60000,
- requestTimeout: 5000,
- responseTimeout: 30000,
- // ===
- // Main Directories
- // https://docs.cypress.io/guides/references/configuration.html#Folders-Files
- // ===
- supportFile: 'tests/e2e/support/setup.js',
- integrationFolder: 'tests/e2e/specs',
- fixturesFolder: 'tests/e2e/fixtures',
- // ===
- // Videos & Screenshots
- // https://docs.cypress.io/guides/core-concepts/screenshots-and-videos.html
- // ===
- videoUploadOnPasses: true,
- videoCompression: 32,
- videosFolder: 'tests/e2e/videos',
- screenshotsFolder: 'tests/e2e/screenshots',
- })
-}
diff --git a/tests/e2e/specs/auth.e2e.js b/tests/e2e/specs/auth.e2e.js
deleted file mode 100644
index 7d2c19b..0000000
--- a/tests/e2e/specs/auth.e2e.js
+++ /dev/null
@@ -1,82 +0,0 @@
-describe('Authentication', () => {
- it('login link exists on the home page when logged out', () => {
- cy.visit('/')
- cy.contains('a', 'Log in').should('have.attr', 'href', '/login')
- })
-
- it('login form shows an error on failure', () => {
- cy.visit('/login')
-
- // Enter bad login info
- cy.get('input[name="username"]').type('badUsername')
- cy.get('input[name="password"]').type('badPassword')
-
- // Submit the login form
- cy.contains('button', 'Log in').click()
-
- // Ensure that an error displays
- cy.contains('error logging in')
- })
-
- it('successful login works redirects to the home page and logging out works', () => {
- cy.visit('/login')
-
- // Enter the user-supplied username and password
- cy.get('input[name="username"]').type('admin')
- cy.get('input[name="password"]').type('password')
-
- // Submit the login form
- cy.contains('button', 'Log in').click()
-
- // Confirm redirection to the homepage
- cy.location('pathname').should('equal', '/')
-
- // Confirm a logout link exists
- cy.contains('a', 'Log out')
- })
-
- it('login after attempting to visit authenticated route redirects to that route after login', () => {
- cy.visit('/profile?someQuery')
-
- // Confirm redirection to the login page
- cy.location('pathname').should('equal', '/login')
-
- // Enter the user-supplied username and password
- cy.get('input[name="username"]').type('admin')
- cy.get('input[name="password"]').type('password')
-
- // Submit the login form
- cy.contains('button', 'Log in').click()
-
- // Confirm redirection to the homepage
- cy.location('pathname').should('equal', '/profile')
- cy.location('search').should('equal', '?someQuery')
-
- // Confirm a logout link exists
- cy.contains('a', 'Log out')
- })
-
- it('logout link logs the user out when logged in', () => {
- cy.logIn()
-
- // Click the logout link
- cy.contains('a', 'Log out').click()
-
- // Confirm that the user is logged out
- cy.contains('a', 'Log in')
- })
-
- it('logout from an authenticated route redirects to home', () => {
- cy.logIn()
- cy.visit('/profile')
-
- // Click the logout link
- cy.contains('a', 'Log out').click()
-
- // Confirm we're on the correct page
- cy.location('pathname').should('equal', '/')
-
- // Confirm that the user is logged out
- cy.contains('a', 'Log in')
- })
-})
diff --git a/tests/e2e/specs/home.e2e.js b/tests/e2e/specs/home.e2e.js
deleted file mode 100644
index f295326..0000000
--- a/tests/e2e/specs/home.e2e.js
+++ /dev/null
@@ -1,7 +0,0 @@
-describe('Home Page', () => {
- it('has the correct title and heading', () => {
- cy.visit('/')
- cy.title().should('equal', 'Home | Vue Enterprise Boilerplate')
- cy.contains('h1', 'Home Page')
- })
-})
diff --git a/tests/e2e/specs/profile.e2e.js b/tests/e2e/specs/profile.e2e.js
deleted file mode 100644
index 6ff540a..0000000
--- a/tests/e2e/specs/profile.e2e.js
+++ /dev/null
@@ -1,33 +0,0 @@
-describe('Profile Page', () => {
- it('redirects to login when logged out', () => {
- cy.visit('/profile')
- cy.location('pathname').should('equal', '/login')
- })
-
- it('nav link exists when logged in', () => {
- cy.logIn()
- cy.contains('a', 'Logged in as Vue Master').should(
- 'have.attr',
- 'href',
- '/profile'
- )
- })
-
- it('shows the current user profile when logged in', () => {
- cy.logIn()
- cy.visit('/profile')
- cy.contains('h1', 'Vue Master')
- })
-
- it('shows non-current users at username routes when logged in', () => {
- cy.logIn()
- cy.visit('/profile/user1')
- cy.contains('h1', 'User One')
- })
-
- it('shows a user 404 page when looking for a user that does not exist', () => {
- cy.logIn()
- cy.visit('/profile/non-existant-user')
- cy.contains('h1', /User\s+Not\s+Found/)
- })
-})
diff --git a/tests/e2e/support/commands.js b/tests/e2e/support/commands.js
deleted file mode 100644
index 2714429..0000000
--- a/tests/e2e/support/commands.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Create custom Cypress commands and overwrite existing ones.
-// https://on.cypress.io/custom-commands
-
-import { getStore } from './utils'
-
-Cypress.Commands.add(
- 'logIn',
- ({ username = 'admin', password = 'password' } = {}) => {
- // Manually log the user in
- cy.location('pathname').then((pathname) => {
- if (pathname === 'blank') {
- cy.visit('/')
- }
- })
- getStore().then((store) =>
- store.dispatch('auth/logIn', { username, password })
- )
- }
-)
diff --git a/tests/e2e/support/setup.js b/tests/e2e/support/setup.js
deleted file mode 100644
index 185d654..0000000
--- a/tests/e2e/support/setup.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// ***********************************************************
-// This example support/index.js 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'
diff --git a/tests/e2e/support/utils.js b/tests/e2e/support/utils.js
deleted file mode 100644
index 60d9588..0000000
--- a/tests/e2e/support/utils.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Returns the Vuex store.
-export const getStore = () => cy.window().its('__app__.$store')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment