diff --git a/.gitignore b/.gitignore index c6d3a6717..bd1efedd1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea bower_components/ /node_modules +cypress/videos diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..11e7b1f03 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: node_js + +node_js: + - 8 + +cache: + directories: + - node_modules + +install: + - npm install + +script: + ## now run cypress headlessly + ## and record all of the tests. + ## Cypress will search for a + ## CYPRESS_PROJECT_ID and CYPRESS_RECORD_KEY environment variables + - npm run test:record + ## see recording of the test run at + ## https://dashboard.cypress.io/#/projects/p7bh2z/runs diff --git a/README.md b/README.md index 98428169d..a3f9f06cb 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ [![License](http://img.shields.io/badge/License-MIT-blue.svg)](http://opensource.org/licenses/MIT) [![PayPal Donate](https://img.shields.io/badge/donate-PayPal.me-ff69b4.svg)](https://www.paypal.me/alvarotrigo/9.95) [![jsDelivr Hits](https://data.jsdelivr.com/v1/package/npm/fullpage.js/badge?style=rounded)](https://www.jsdelivr.com/package/npm/fullpage.js) +[![Cypress.io tests](https://img.shields.io/badge/cypress.io-tests-green.svg?style=flat-square)](https://dashboard.cypress.io/#/projects/p7bh2z/runs) +[![Build Status](https://travis-ci.org/bahmutov/fullPage.js.svg?branch=master)](https://travis-ci.org/bahmutov/fullPage.js)    **|**   *7Kb gziped*   **|**   *Created by [@imac2](https://twitter.com/imac2)* - [Live demo](http://alvarotrigo.com/fullPage/) @@ -877,6 +879,24 @@ To see the list of recent changes, see [Releases section](https://github.com/alv # Build tasks Want to build fullpage.js distribution files? Please see [Build Tasks](https://github.com/alvarotrigo/fullPage.js/wiki/Build-tasks) +# Testing + +To run end to end tests in a real browser (Electron / Chrome) this project uses [Cypress.io](https://github.com/cypress-io/cypress). To run tests on CI simple execute + +```shell +$ npm test +``` + +Which starts static server (using `npm start`), runs E2E tests and then closes the server. + +To see E2E tests live use + +```shell +$ npm run test:gui +``` + +The Cypress runner keeps watching your test files, rerunning the tests as you are editing the spec files. Find the spec files in the [cypress/integration](cypress/integration) folder. The tests are also running on [CI](https://travis-ci.org/bahmutov/fullPage.js) and the output and video recordings can be found on [Cypress dashboard](https://dashboard.cypress.io/#/projects/p7bh2z/runs) + # Resources - [Wordpress theme](http://alvarotrigo.com/fullPage/utils/wordpress.html) - [CSS Easing Animation Tool - Matthew Lein](http://matthewlein.com/ceaser/) (useful to define the `easingcss3` value) diff --git a/cypress.json b/cypress.json new file mode 100644 index 000000000..05d095b8b --- /dev/null +++ b/cypress.json @@ -0,0 +1,3 @@ +{ + "baseUrl": "http://localhost:4600" +} diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 000000000..da18d9352 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/integration/methods_spec.js b/cypress/integration/methods_spec.js new file mode 100644 index 000000000..a72682028 --- /dev/null +++ b/cypress/integration/methods_spec.js @@ -0,0 +1,81 @@ +import { checkActiveSlide } from '../support' + +/* eslint-env mocha */ +/* global cy */ +describe('simple example', () => { + beforeEach(() => { + cy.visit('/examples/methods') + cy.get('.fullpage-wrapper').should('be.visible') + }) + + const getMenuButtons = () => cy.get('ul#menu li') + + const getMenuButton = title => getMenuButtons().contains(title) + + it('has slide buttons', () => { + // 4 buttons on the top + getMenuButtons().should('have.length', 4) + getMenuButtons().contains('First slide') + getMenuButton('Second slide') + getMenuButton('Third slide') + getMenuButton('Fourth slide') + }) + + it('is at first slide at first', () => { + checkActiveSlide('Section 1') + }) + + it('goes to second slide by clicking on menu button', () => { + getMenuButton('Second slide').click() + checkActiveSlide('Section 2') + }) + + it('goes to third slide and fourth slide', () => { + getMenuButton('Third slide').click() + checkActiveSlide('Section 3') + + getMenuButton('Fourth slide').click() + checkActiveSlide('Section 4') + }) + + it('does not url hash initially', () => { + cy.hash().should('eq', '') + }) + + it('changes url hash after transition', () => { + getMenuButton('Second slide').click() + checkActiveSlide('Section 2') + cy.hash().should('eq', '#secondPage') + }) + + context('actions', () => { + const getActions = () => cy.get('#actions').should('not.be', 'empty') + + const getAction = title => getActions().contains(title) + + it('has actions', () => { + getActions() + }) + + it('slides down', () => { + getAction('moveSectionDown').click() + checkActiveSlide('Section 2') + }) + + it('moves to slide 2/3', () => { + getAction('moveTo(2,3)').click() + checkActiveSlide('Slide 2.3') + cy.hash().should('eq', '#secondPage/3') + }) + + it('destroys all', () => { + getAction("destroy('all')").click() + cy.get('.fullpage-wrapper').should('have.class', 'fp-destroyed') + }) + + it('undestroys', () => { + getAction('undestroy').click() + cy.get('.fullpage-wrapper').should('not.have.class', 'fp-destroyed') + }) + }) +}) diff --git a/cypress/integration/simple_spec.js b/cypress/integration/simple_spec.js new file mode 100644 index 000000000..a76b05fda --- /dev/null +++ b/cypress/integration/simple_spec.js @@ -0,0 +1,74 @@ +import { Arrows, slide, checkActiveSlide, activeSlide } from '../support' + +/* eslint-env mocha */ +/* global cy */ +describe('simple example', () => { + beforeEach(() => { + cy.visit('/examples/simple') + cy.get('#fullpage').should('be.visible') + }) + + it('loads', () => { + cy.contains('fullPage.js') + }) + + it('hides second page', () => { + cy.contains('Only text').should('not.be', 'visible') + }) + + it('goes down to second slide', () => { + // cy.document().its('body').trigger('keydown', { which: Arrows.down }) + cy.document().its('body').type('{downarrow}') + cy.contains('.section', 'Only text').should('have.class', 'active') + }) + + it('goes down to 2nd slide - auto wait', () => { + slide.down() + checkActiveSlide('Only text') + }) + + it('goes down to 4th slide', () => { + slide.down() + slide.down() + slide.down() + checkActiveSlide('Just the simplest demo ever') + }) + + it('does not have left or right arrows on the first slide', () => { + activeSlide().find('.fp-prev').should('not.exist') + activeSlide().find('.fp-next').should('not.exist') + }) + + it('shows left and right arrows', () => { + slide.down() + checkActiveSlide('Only text') + activeSlide().find('.fp-prev').should('be.visible') + activeSlide().find('.fp-next').should('be.visible') + }) + + it('goes horizontally', () => { + slide.down() + checkActiveSlide('Only text') + slide.right() + checkActiveSlide('And text') + slide.right() + checkActiveSlide('And more text') + slide.left() + checkActiveSlide('And text') + slide.down() + checkActiveSlide('No wraps, no extra markup') + }) + + it('loops horizontally', () => { + slide.down() + checkActiveSlide('Only text') + slide.right() + checkActiveSlide('And text') + slide.right() + checkActiveSlide('And more text') + slide.right() + checkActiveSlide('Simple Demo') + slide.right() + checkActiveSlide('Only text') + }) +}) diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 000000000..fd170fba6 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 000000000..c1f5a772e --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js 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 is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 000000000..eb12f09e0 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,60 @@ +// *********************************************************** +// 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' + +// Alternatively you can use CommonJS syntax: +// require('./commands') +/* global cy */ + +export const Arrows = { + up: '{uparrow}', + down: '{downarrow}', + left: '{leftarrow}', + right: '{rightarrow}' +} + +function triggerArrow (arrow) { + cy.document().its('body').type(arrow) + // wait for a second. If there was consistent _unique_ CSS selector + // for "transition going on", and then "transition finished" + // we could select on that to avoid hard coded duration + cy.wait(1000) +} + +export const slide = { + down () { + triggerArrow(Arrows.down) + }, + + up () { + triggerArrow(Arrows.up) + }, + + left () { + triggerArrow(Arrows.left) + }, + + right () { + triggerArrow(Arrows.right) + } +} + +export function checkActiveSlide (text) { + cy.contains('.section', text).should('have.class', 'active') +} + +export const activeSlide = () => cy.get('.section.active') diff --git a/package.json b/package.json index a5af685c0..c03f47c70 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,13 @@ "description": "Create beautiful fullscreen scrolling websites", "main": "dist/jquery.fullpage.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "run-p --race start cy:run", + "test:record": "run-p --race start cy:run:record", + "test:gui": "run-p --race start cy:open", + "start": "http-server -c-1 -p 4600 --silent", + "cy:open": "cypress open", + "cy:run": "cypress run", + "cy:run:record": "cypress run --record" }, "repository": { "type": "git", @@ -42,11 +48,14 @@ "homepage": "https://github.com/alvarotrigo/fullPage.js", "namespace": "$.fn.fullpage", "devDependencies": { + "cypress": "^1.1.4", "gulp": "^3.9.0", "gulp-clean-css": "^2.0.8", "gulp-rename": "^1.2.2", "gulp-sass": "^2.0.4", "gulp-sourcemaps": "^1.6.0", - "gulp-uglify": "^2.1.2" + "gulp-uglify": "^2.1.2", + "http-server": "^0.10.0", + "npm-run-all": "^4.1.2" } }