From 391455bee4dc5206f1cea264b7066016519270d6 Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Mon, 19 Feb 2018 18:24:22 -0800 Subject: [PATCH] switch from cypress to testcafe --- .travis.yml | 11 - README.md | 6 +- appveyor.yml | 18 - cypress.json | 5 - cypress/fixtures/example.json | 5 - cypress/integration/01-basic-spec.js | 31 - cypress/integration/02-login-spec.js | 38 -- cypress/integration/03-basic-timeline-spec.js | 101 ---- cypress/integration/04-pinned-statuses.js | 36 -- cypress/integration/05-status-types.js | 49 -- cypress/integration/06-tabindex.js | 45 -- cypress/integration/07-account-profile.js | 41 -- cypress/plugins/index.js | 17 - cypress/support/commands.js | 80 --- cypress/support/index.js | 20 - package-lock.json | 569 ------------------ package.json | 17 +- tests/spec/02-login-spec.js | 19 +- tests/spec/04-pinned-statuses.js | 35 ++ tests/spec/05-status-types.js | 44 ++ tests/spec/06-tabindex.js | 39 ++ tests/spec/07-account-profile.js | 39 ++ 22 files changed, 180 insertions(+), 1085 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml delete mode 100644 cypress.json delete mode 100644 cypress/fixtures/example.json delete mode 100644 cypress/integration/01-basic-spec.js delete mode 100644 cypress/integration/02-login-spec.js delete mode 100644 cypress/integration/03-basic-timeline-spec.js delete mode 100644 cypress/integration/04-pinned-statuses.js delete mode 100644 cypress/integration/05-status-types.js delete mode 100644 cypress/integration/06-tabindex.js delete mode 100644 cypress/integration/07-account-profile.js delete mode 100644 cypress/plugins/index.js delete mode 100644 cypress/support/commands.js delete mode 100644 cypress/support/index.js create mode 100644 tests/spec/04-pinned-statuses.js create mode 100644 tests/spec/05-status-types.js create mode 100644 tests/spec/06-tabindex.js create mode 100644 tests/spec/07-account-profile.js diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8ad1fc3..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -sudo: false -language: node_js -node_js: - - "stable" -env: - global: - - BUILD_TIMEOUT=10000 -install: - - npm install - - npm install cypress - diff --git a/README.md b/README.md index 9207022..7960a6a 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ In separate terminals: npm run dev -3\. Run a Cypress dev environment: +3\. Run a debuggable TestCafé instance: - npm run cy:open + npx testcafe --hostname localhost --skip-js-errors --debug-mode chrome tests/spec If you want to export the current data in the Mastodon instance as canned data, so that it can be loaded later: @@ -38,6 +38,6 @@ Lint: npm run lint -Run Cypress tests: +Run integration tests: npm test \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index e75da3b..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: "{build}" - -shallow_clone: true - -init: - - git config --global core.autocrlf false - -build: off - -environment: - matrix: - # node.js - - nodejs_version: stable - -install: - - ps: Install-Product node $env:nodejs_version - - npm install cypress - - npm install diff --git a/cypress.json b/cypress.json deleted file mode 100644 index b54458d..0000000 --- a/cypress.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "baseUrl": "http://localhost:4002", - "videoRecording": false, - "chromeWebSecurity": false -} \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index da18d93..0000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "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/01-basic-spec.js b/cypress/integration/01-basic-spec.js deleted file mode 100644 index 34c4791..0000000 --- a/cypress/integration/01-basic-spec.js +++ /dev/null @@ -1,31 +0,0 @@ -describe('01-basic-spec.js', () => { - beforeEach(() => { - cy.visit('/') - cy.wait(500) - }) - - it('has the correct

', () => { - cy.contains('h1', 'Pinafore') - }) - - it('navigates to about', () => { - cy.get('nav a[aria-label=Settings]').click() - cy.url().should('contain', '/settings') - cy.get('a').contains('About').click() - cy.url().should('contain', '/settings/about') - cy.contains('h1', 'About Pinafore') - }) - - it('navigates to /settings/instances/add', () => { - cy.contains('log in to an instance').click() - cy.url().should('contain', '/settings/instances/add') - }) - - it('navigates to settings/instances', () => { - cy.get('nav a[aria-label=Settings]').click() - cy.url().should('contain', '/settings') - cy.get('a').contains('Instances').click() - cy.url().should('contain', '/settings/instances') - cy.contains("You're not logged in to any instances") - }) -}) diff --git a/cypress/integration/02-login-spec.js b/cypress/integration/02-login-spec.js deleted file mode 100644 index 45e76a5..0000000 --- a/cypress/integration/02-login-spec.js +++ /dev/null @@ -1,38 +0,0 @@ -describe('02-login-spec.js', () => { - beforeEach(() => { - cy.visit('/') - cy.wait(500) - }) - - it('Cannot log in to a fake instance', () => { - cy.get('a').contains('log in to an instance').click() - - cy.get('#instanceInput').clear().type('fake.nolanlawson.com') - cy.get('.add-new-instance').submit() - cy.get('.form-error', {timeout: 20000}).contains('Is this a valid Mastodon instance?') - cy.get('#instanceInput').type('.biz') - cy.get('.form-error').should('not.exist') - cy.get('#instanceInput').clear().type('fake.nolanlawson.com') - cy.get('.form-error').should('exist') - }) - - it('Logs in to localhost:3000', () => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - - cy.url().should('equal', 'http://localhost:4002/') - cy.get('article.status-article').should('exist') - }) - - it('Logs out', () => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - cy.get('nav a[aria-label=Settings]').click() - cy.get('a').contains('Instances').click() - cy.get('a').contains('localhost:3000').click() - cy.url().should('contain', '/settings/instances/localhost:3000') - cy.get('button').contains('Log out').click() - cy.get('#modal-dialog button').contains('OK').click() - cy.url().should('contain', '/settings/instances') - cy.contains("You're not logged in to any instances") - }) -}) diff --git a/cypress/integration/03-basic-timeline-spec.js b/cypress/integration/03-basic-timeline-spec.js deleted file mode 100644 index 3e079f2..0000000 --- a/cypress/integration/03-basic-timeline-spec.js +++ /dev/null @@ -1,101 +0,0 @@ -const times = require('lodash/times') - -describe('03-basic-timeline-spec.js', () => { - beforeEach(() => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - }) - - const homeTimeline = [ - {content: 'pinned toot 1'}, - {content: 'notification of unlisted message'}, - {content: 'notification of followers-only message'}, - {content: 'notification of direct message'}, - {content: 'this is unlisted'}, - {content: 'this is followers-only'}, - {content: 'direct'}, - {spoiler: 'kitten CW'}, - {content: 'secret video'}, - {content: "here's a video"}, - {spoiler: 'CW'}, - {content: "here's a secret animated kitten gif"}, - {content: "here's an animated kitten gif"}, - {content: "here's 2 kitten photos"}, - {content: "here's a secret kitten"}, - {content: "here's a kitten"}, - {content: 'hello admin'}, - {content: 'hello foobar'}, - {content: 'hello world'} - ].concat(times(30, i => ({content: (30 - i).toString()}))) - - const localTimeline = [ - {spoiler: 'kitten CW'}, - {content: 'secret video'}, - {content: "here's a video"}, - {spoiler: 'CW'}, - {content: "here's a secret animated kitten gif"}, - {content: "here's an animated kitten gif"}, - {content: "here's 2 kitten photos"}, - {content: "here's a secret kitten"}, - {content: "here's a kitten"}, - {content: 'hello world'} - ].concat(times(30, i => ({content: (30 - i).toString()}))) - - const notifications = [ - {favoritedBy: 'admin'}, - {rebloggedBy: 'admin'}, - {content: 'notification of unlisted message'}, - {content: 'notification of followers-only message'}, - {content: 'notification of direct message'}, - {followedBy: 'quux'}, - {content: 'hello foobar'}, - {followedBy: 'admin'} - ] - - const favorites = [ - {content: 'notification of direct message'}, - {content: 'notification of followers-only message'}, - {content: 'notification of unlisted message'}, - {content: 'pinned toot 1'} - ] - - it('Shows the home timeline', () => { - cy.get('.virtual-list-item[aria-hidden=false] .status-article:first').should('have.attr', 'aria-setsize') - cy.get('.virtual-list-item[aria-hidden=false] .status-article:first').should('have.attr', 'aria-posinset', '0') - - cy.validateTimeline(homeTimeline) - - cy.get('.virtual-list-item[aria-hidden=false] .status-article:first').should('have.attr', 'aria-setsize', '49') - }) - - it('Shows notifications', () => { - cy.get('nav a[aria-label=Notifications]').click() - cy.url().should('contain', '/notifications') - - cy.validateTimeline(notifications) - }) - - it('Shows the local timeline', () => { - cy.get('nav a[aria-label=Local]').click() - cy.url().should('contain', '/local') - - cy.validateTimeline(localTimeline) - }) - - it('Shows the federated timeline', () => { - cy.get('nav a[aria-label=Community]').click() - cy.url().should('contain', '/community') - cy.get('a').contains('Federated').click() - cy.url().should('contain', '/federated') - - cy.validateTimeline(localTimeline) // local is same as federated in this case - }) - - it('Shows favorites', () => { - cy.get('nav a[aria-label=Community]').click() - cy.url().should('contain', '/community') - cy.get('a').contains('Favorites').click() - cy.url().should('contain', '/favorites') - cy.validateTimeline(favorites) - }) -}) diff --git a/cypress/integration/04-pinned-statuses.js b/cypress/integration/04-pinned-statuses.js deleted file mode 100644 index 6e5407b..0000000 --- a/cypress/integration/04-pinned-statuses.js +++ /dev/null @@ -1,36 +0,0 @@ -describe('04-pinned-statuses.js', () => { - beforeEach(() => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - }) - - it("shows a user's pinned statuses", () => { - cy.get('nav a[aria-label=Community]').click() - cy.url().should('contain', '/community') - cy.get('a').contains('Pinned').click() - cy.url().should('contain', '/pinned') - - cy.get('.status-article').should('have.attr', 'aria-posinset', '0') - cy.get('.status-article').should('have.attr', 'aria-setsize', '1') - cy.get('.status-article .status-content').should('contain', 'this is unlisted') - }) - - it("shows pinned statuses on a user's account page", () => { - cy.visit('/accounts/2') - cy.wait(500) - cy.get('.pinned-statuses .status-article').should('have.attr', 'aria-posinset', '0') - cy.get('.pinned-statuses .status-article').should('have.attr', 'aria-setsize', '1') - cy.get('.pinned-statuses .status-article').should('contain', 'this is unlisted') - }) - - it("shows pinned statuses on a user's account page 2", () => { - cy.visit('/accounts/3') - cy.wait(500) - cy.get('.pinned-statuses .status-article').should('have.attr', 'aria-posinset', '0') - cy.get('.pinned-statuses .status-article').should('have.attr', 'aria-setsize', '2') - cy.get('.pinned-statuses .status-article').should('contain', 'pinned toot 1') - - cy.get('.pinned-statuses .status-article[aria-posinset=1]').should('have.attr', 'aria-setsize', '2') - cy.get('.pinned-statuses .status-article[aria-posinset=1]').should('contain', 'pinned toot 2') - }) -}) diff --git a/cypress/integration/05-status-types.js b/cypress/integration/05-status-types.js deleted file mode 100644 index cf2b8dd..0000000 --- a/cypress/integration/05-status-types.js +++ /dev/null @@ -1,49 +0,0 @@ -describe('05-status-types.js', () => { - beforeEach(() => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - }) - - it('shows direct vs followers-only vs regular', () => { - cy.getNthVirtualArticle(1).should('have.attr', 'aria-label', 'Status by admin') - cy.getNthVirtualArticle(1).find('.status-content').should('contain', 'notification of unlisted message') - cy.getNthVirtualArticle(1).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Boost') - .and('not.have.attr', 'disabled') - - cy.getNthVirtualArticle(2).should('have.attr', 'aria-label', 'Status by admin') - cy.getNthVirtualArticle(2).find('.status-content').should('contain', 'notification of followers-only message') - cy.getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Cannot be boosted because this is followers-only') - .and('have.attr', 'disabled') - - cy.getNthVirtualArticle(3).should('have.attr', 'aria-label', 'Direct message by admin') - cy.getNthVirtualArticle(3).find('.status-content').should('contain', 'notification of direct message') - cy.getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Cannot be boosted because this is a direct message') - .and('have.attr', 'disabled') - }) - - it('shows direct vs followers-only vs regular in notifications', () => { - cy.visit('/notifications') - cy.wait(500) - - cy.getNthVirtualArticle(2).should('have.attr', 'aria-label', 'Status by admin') - cy.getNthVirtualArticle(2).find('.status-content').should('contain', 'notification of unlisted message') - cy.getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Boost') - .and('not.have.attr', 'disabled') - - cy.getNthVirtualArticle(3).should('have.attr', 'aria-label', 'Status by admin') - cy.getNthVirtualArticle(3).find('.status-content').should('contain', 'notification of followers-only message') - cy.getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Cannot be boosted because this is followers-only') - .and('have.attr', 'disabled') - - cy.getNthVirtualArticle(4).should('have.attr', 'aria-label', 'Direct message by admin') - cy.getNthVirtualArticle(4).find('.status-content').should('contain', 'notification of direct message') - cy.getNthVirtualArticle(4).find('.status-toolbar button:nth-child(2)') - .should('have.attr', 'aria-label', 'Cannot be boosted because this is a direct message') - .and('have.attr', 'disabled') - }) -}) diff --git a/cypress/integration/06-tabindex.js b/cypress/integration/06-tabindex.js deleted file mode 100644 index 4d8663d..0000000 --- a/cypress/integration/06-tabindex.js +++ /dev/null @@ -1,45 +0,0 @@ -describe('06-tabindex.js', () => { - beforeEach(() => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - }) - - it('shows correct tabindex in home timeline', () => { - cy.getNthVirtualArticle(0).should('have.attr', 'tabindex', '0') - cy.getNthVirtualArticle(2).should('have.attr', 'tabindex', '0') - cy.getNthVirtualArticle(3).should('have.attr', 'tabindex', '0') - }) - - it('shows correct tabindex in notifications', () => { - cy.visit('/notifications') - cy.wait(500) - cy.getNthVirtualArticle(0).should('have.attr', 'tabindex', '0') - .and('have.class', 'status-article') - cy.getNthVirtualArticle(1).should('have.attr', 'tabindex', '0') - .and('have.class', 'status-article') - cy.getNthVirtualArticle(2).should('have.attr', 'tabindex', '0') - .and('have.class', 'status-article') - cy.getNthVirtualArticle(3).should('have.attr', 'tabindex', '0') - .and('have.class', 'status-article') - cy.getNthVirtualArticle(4).should('have.attr', 'tabindex', '0') - cy.getNthVirtualArticle(4).scrollIntoView() - cy.wait(500) - cy.getNthVirtualArticle(5).should('have.attr', 'tabindex', '0') - .and('have.class', 'notification-article') - cy.getNthVirtualArticle(6).should('have.attr', 'tabindex', '0') - .and('have.class', 'status-article') - cy.getNthVirtualArticle(6).scrollIntoView() - cy.wait(500) - cy.getNthVirtualArticle(7).should('have.attr', 'tabindex', '0') - .and('have.attr', 'aria-setsize', '8') - .and('have.class', 'notification-article') - }) - - it('shows correct tabindex in pinned statuses', () => { - cy.visit('/pinned') - cy.wait(500) - cy.get('.status-article').should('have.attr', 'tabindex', '0') - .and('have.attr', 'aria-posinset', '0') - .and('have.attr', 'aria-setsize', '1') - }) -}) diff --git a/cypress/integration/07-account-profile.js b/cypress/integration/07-account-profile.js deleted file mode 100644 index 943dcc8..0000000 --- a/cypress/integration/07-account-profile.js +++ /dev/null @@ -1,41 +0,0 @@ -describe('07-account-profile.js', () => { - beforeEach(() => { - cy.login('foobar@localhost:3000', 'foobarfoobar') - cy.wait(500) - }) - - it('shows account profile', () => { - cy.get('.status-author-name').contains('quux').click() - cy.url().should('contain', '/accounts/3') - cy.wait(500) - cy.get('.account-profile .account-profile-name').should('contain', 'quux') - cy.get('.account-profile .account-profile-username').should('contain', '@quux') - cy.get('.account-profile .account-profile-followed-by').should('contain', 'Follows you') - cy.get('.account-profile .account-profile-follow button') - .should('have.attr', 'aria-label', 'Follow') - .and('have.attr', 'aria-pressed', 'false') - }) - - it('shows account profile 2', () => { - cy.get('.status-author-name').contains('admin').click() - cy.url().should('contain', '/accounts/1') - cy.wait(500) - cy.get('.account-profile .account-profile-name').should('contain', 'admin') - cy.get('.account-profile .account-profile-username').should('contain', '@admin') - cy.get('.account-profile .account-profile-followed-by').should('contain', 'Follows you') - cy.get('.account-profile .account-profile-follow button') - .should('have.attr', 'aria-label', 'Unfollow') - .and('have.attr', 'aria-pressed', 'true') - }) - - it('shows account profile 3', () => { - cy.get('.mention').contains('foobar').click() - cy.url().should('contain', '/accounts/2') - cy.wait(500) - cy.get('.account-profile .account-profile-name').should('contain', 'foobar') - cy.get('.account-profile .account-profile-username').should('contain', '@foobar') - // can't follow or be followed by your own account - cy.get('.account-profile .account-profile-followed-by').should('be.empty') - cy.get('.account-profile .account-profile-follow').should('be.empty') - }) -}) diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js deleted file mode 100644 index fd170fb..0000000 --- a/cypress/plugins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// 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 deleted file mode 100644 index ea8c67e..0000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,80 +0,0 @@ -// *********************************************** -// 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) => { ... }) - -Cypress.Commands.add('login', (email, password) => { - // mastodon throws some uncaught TypeErrors - cy.on('uncaught:exception', (err) => { - expect(err.name).to.include('TypeError') - expect(err.message).to.include('Illegal invocation') - return false - }) - - cy.visit('/settings/instances/add') - cy.wait(500) - cy.get('#instanceInput').clear().type('localhost:3000') - cy.get('.add-new-instance').submit() - cy.wait(500) - cy.url().should('equal', 'http://localhost:3000/auth/sign_in') - cy.get('input#user_email').should('exist') - cy.get('input#user_password').should('exist') - cy.get('input#user_email').type(email) - cy.get('input#user_password').type(password) - cy.get('form#new_user').submit() - cy.url().should('contain', '/oauth/authorize') - - cy.get('button').contains('Authorize').click() - cy.url().should('equal', 'http://localhost:4002/') -}) - -Cypress.Commands.add('getNthVirtualArticle', (n) => { - return cy.get(`.virtual-list-item[aria-hidden=false] article[aria-posinset=${n}]`) -}) - -Cypress.Commands.add('validateTimeline', (timeline) => { - timeline.forEach((status, i) => { - if (status.content) { - cy.getNthVirtualArticle(i).find('.status-content p').should('contain', status.content) - } - if (status.spoiler) { - cy.getNthVirtualArticle(i).find('.status-spoiler p').should('contain', status.spoiler) - } - if (status.followedBy) { - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', status.followedBy) - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', 'followed you') - } - if (status.rebloggedBy) { - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', status.rebloggedBy) - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', 'boosted') - } - if (status.favoritedBy) { - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', status.favoritedBy) - cy.getNthVirtualArticle(i).find('.status-header span').should('contain', 'favorited') - } - cy.wait(50) - cy.getNthVirtualArticle(i).scrollIntoView() - cy.get('.loading-footer').should('not.exist') - }) -}) diff --git a/cypress/support/index.js b/cypress/support/index.js deleted file mode 100644 index d68db96..0000000 --- a/cypress/support/index.js +++ /dev/null @@ -1,20 +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' - -// Alternatively you can use CommonJS syntax: -// require('./commands') diff --git a/package-lock.json b/package-lock.json index 49232af..ac4904e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,54 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@cypress/listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" - } - }, - "@cypress/xvfb": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.1.3.tgz", - "integrity": "sha512-EfRzw+wgI0Zdb4ZlhSvjh3q7I+oenqEYPXvr7oH/2RnzQqGDrPr7IU1Pi2yzGwoXmkNUQbo6qvntnItvQj0F4Q==", - "requires": { - "lodash.once": "4.1.1" - } - }, "@gamestdio/websocket": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@gamestdio/websocket/-/websocket-0.2.2.tgz", "integrity": "sha512-ptWTKBq6IliC7lNhfAI/YI5WBjhPClflZV61V7In/qxGbgrWrlny6/Df4NtrUs7QUeNccv8yqQwjjw+f0veGjQ==" }, - "@types/blob-util": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/blob-util/-/blob-util-1.3.3.tgz", - "integrity": "sha512-4ahcL/QDnpjWA2Qs16ZMQif7HjGP2cw3AGjHabybjw7Vm1EKu+cfQN1D78BaZbS1WJNa1opSMF5HNMztx7lR0w==" - }, - "@types/bluebird": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.18.tgz", - "integrity": "sha512-OTPWHmsyW18BhrnG5x8F7PzeZ2nFxmHGb42bZn79P9hl+GI5cMzyPgQTwNjbem0lJhoru/8vtjAFCUOu3+gE2w==" - }, - "@types/chai": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.8.tgz", - "integrity": "sha512-m812CONwdZn/dMzkIJEY0yAs4apyTkTORgfB2UsMOxgkUbC205AHnm4T8I0I5gPg9MHrFc1dJ35iS75c0CJkjg==" - }, - "@types/chai-jquery": { - "version": "1.1.35", - "resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.35.tgz", - "integrity": "sha512-7aIt9QMRdxuagLLI48dPz96YJdhu64p6FCa6n4qkGN5DQLHnrIjZpD9bXCvV2G0NwgZ1FAmfP214dxc5zNCfgQ==", - "requires": { - "@types/chai": "4.0.8", - "@types/jquery": "2.0.48" - } - }, "@types/chalk": { "version": "0.4.31", "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz", @@ -72,35 +29,11 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.87.tgz", "integrity": "sha512-AqRC+aEF4N0LuNHtcjKtvF9OTfqZI0iaBoe3dA6m/W+/YZJBZjBmW/QIZ8fBeXC6cnytSY9tBoFBqZ9uSCeVsw==" }, - "@types/minimatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", - "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==" - }, - "@types/mocha": { - "version": "2.2.44", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.44.tgz", - "integrity": "sha512-k2tWTQU8G4+iSMvqKi0Q9IIsWAp/n8xzdZS4Q4YVIltApoMA00wFBFdlJnmoaK1/z7B0Cy0yPe6GgXteSmdUNw==" - }, "@types/node": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.0.tgz", "integrity": "sha512-zkYho6/4wZyX6o9UQ8rd0ReEaiEYNNCqYFIAACe2Tf9DrYlgzWW27OigYHnnztnnZQwVRpwWmZKegFmDpinIsA==" }, - "@types/sinon": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.0.0.tgz", - "integrity": "sha512-cuK4xM8Lg2wd8cxshcQa8RG4IK/xfyB6TNE6tNVvkrShR4xdrYgsV04q6Dp6v1Lp6biEFdzD8k8zg/ujQeiw+A==" - }, - "@types/sinon-chai": { - "version": "2.7.29", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-2.7.29.tgz", - "integrity": "sha512-EkI/ZvJT4hglWo7Ipf9SX+J+R9htNOMjW8xiOhce7+0csqvgoF5IXqY5Ae1GqRgNtWCuaywR5HjVa1snkTqpOw==", - "requires": { - "@types/chai": "4.0.8", - "@types/sinon": "4.0.0" - } - }, "a11y-dialog": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/a11y-dialog/-/a11y-dialog-4.0.1.tgz", @@ -1410,11 +1343,6 @@ "isarray": "1.0.0" } }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -1595,11 +1523,6 @@ "supports-color": "2.0.0" } }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=" - }, "cheerio": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", @@ -1805,32 +1728,6 @@ "restore-cursor": "1.0.1" } }, - "cli-spinners": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", - "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=" - }, - "cli-truncate": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", - "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", - "requires": { - "slice-ansi": "0.0.4", - "string-width": "1.0.2" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", @@ -1937,14 +1834,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" }, - "common-tags": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.4.0.tgz", - "integrity": "sha1-EYe+Tz1M8MBCfUP3Tu8fc1AWFMA=", - "requires": { - "babel-runtime": "6.26.0" - } - }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -2345,195 +2234,6 @@ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" }, - "cypress": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-2.0.2.tgz", - "integrity": "sha512-ciIOchZSnvlkwB6cxjeK9TycKVHAA3eE/PJYq7CSnLWkmepkwxASRqD5RPcXgefKVJB5rS92RkKIq2CBOZGcdQ==", - "requires": { - "@cypress/listr-verbose-renderer": "0.4.1", - "@cypress/xvfb": "1.1.3", - "@types/blob-util": "1.3.3", - "@types/bluebird": "3.5.18", - "@types/chai": "4.0.8", - "@types/chai-jquery": "1.1.35", - "@types/jquery": "3.2.16", - "@types/lodash": "4.14.87", - "@types/minimatch": "3.0.1", - "@types/mocha": "2.2.44", - "@types/sinon": "4.0.0", - "@types/sinon-chai": "2.7.29", - "bluebird": "3.5.0", - "chalk": "2.1.0", - "check-more-types": "2.24.0", - "commander": "2.11.0", - "common-tags": "1.4.0", - "debug": "3.1.0", - "extract-zip": "1.6.6", - "fs-extra": "4.0.1", - "getos": "2.8.4", - "glob": "7.1.2", - "is-ci": "1.0.10", - "is-installed-globally": "0.1.0", - "lazy-ass": "1.6.0", - "listr": "0.12.0", - "lodash": "4.17.4", - "minimist": "1.2.0", - "progress": "1.1.8", - "ramda": "0.24.1", - "request": "2.81.0", - "request-progress": "0.3.1", - "supports-color": "5.1.0", - "tmp": "0.0.31", - "url": "0.11.0", - "yauzl": "2.8.0" - }, - "dependencies": { - "@types/jquery": { - "version": "3.2.16", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.2.16.tgz", - "integrity": "sha512-q2WC02YxQoX2nY1HRKlYGHpGP1saPmD7GN0pwCDlTz35a4eOtJG+aHRlXyjCuXokUukSrR2aXyBhSW3j+jPc0A==" - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "requires": { - "color-convert": "1.9.1" - } - }, - "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" - }, - "dependencies": { - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - } - }, - "supports-color": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", - "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", - "requires": { - "has-flag": "2.0.0" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.1" - } - } - } - }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -2557,11 +2257,6 @@ } } }, - "date-fns": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", - "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==" - }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -3506,35 +3201,6 @@ "webpack-sources": "1.1.0" } }, - "extract-zip": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz", - "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=", - "requires": { - "concat-stream": "1.6.0", - "debug": "2.6.9", - "mkdirp": "0.5.0", - "yauzl": "2.4.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "requires": { - "minimist": "0.0.8" - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "requires": { - "fd-slicer": "1.0.1" - } - } - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -3560,14 +3226,6 @@ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "requires": { - "pend": "1.2.0" - } - }, "fg-loadcss": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fg-loadcss/-/fg-loadcss-2.0.1.tgz", @@ -3835,16 +3493,6 @@ "readable-stream": "2.3.3" } }, - "fs-extra": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.1.tgz", - "integrity": "sha1-f8DGyJV/mD9X8waiTlud3Y0N2IA=", - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "3.0.1", - "universalify": "0.1.1" - } - }, "fs-write-stream-atomic": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", @@ -4734,24 +4382,6 @@ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, - "getos": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/getos/-/getos-2.8.4.tgz", - "integrity": "sha1-e4YD02GcKOOMsP56T2PDrLgNUWM=", - "requires": { - "async": "2.1.4" - }, - "dependencies": { - "async": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", - "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", - "requires": { - "lodash": "4.17.4" - } - } - } - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -4831,14 +4461,6 @@ } } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "requires": { - "ini": "1.3.5" - } - }, "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", @@ -4887,11 +4509,6 @@ "duplexer": "0.1.1" } }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" - }, "har-validator": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", @@ -5236,11 +4853,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, "inquirer": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", @@ -5454,15 +5066,6 @@ "is-extglob": "2.1.1" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "requires": { - "global-dirs": "0.1.1", - "is-path-inside": "1.0.1" - } - }, "is-my-json-valid": { "version": "2.17.1", "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz", @@ -5541,11 +5144,6 @@ "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, "is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -5688,14 +5286,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, - "jsonfile": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", - "requires": { - "graceful-fs": "4.1.11" - } - }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -5737,11 +5327,6 @@ "is-buffer": "1.1.6" } }, - "lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=" - }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -5772,67 +5357,6 @@ "os-family": "1.0.0" } }, - "listr": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/listr/-/listr-0.12.0.tgz", - "integrity": "sha1-a84sD1YD+klYDqF81qAMwOX6RRo=", - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "figures": "1.7.0", - "indent-string": "2.1.0", - "is-promise": "2.1.0", - "is-stream": "1.1.0", - "listr-silent-renderer": "1.1.1", - "listr-update-renderer": "0.2.0", - "listr-verbose-renderer": "0.4.1", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "ora": "0.2.3", - "p-map": "1.2.0", - "rxjs": "5.5.6", - "stream-to-observable": "0.1.0", - "strip-ansi": "3.0.1" - } - }, - "listr-silent-renderer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", - "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=" - }, - "listr-update-renderer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz", - "integrity": "sha1-yoDhd5tOcCZoB+ju0a1qvjmFUPk=", - "requires": { - "chalk": "1.1.3", - "cli-truncate": "0.2.1", - "elegant-spinner": "1.0.1", - "figures": "1.7.0", - "indent-string": "3.2.0", - "log-symbols": "1.0.2", - "log-update": "1.0.2", - "strip-ansi": "3.0.1" - }, - "dependencies": { - "indent-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", - "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=" - } - } - }, - "listr-verbose-renderer": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", - "integrity": "sha1-ggb0z21S3cWCfl/RSYng6WWTOjU=", - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "date-fns": "1.29.0", - "figures": "1.7.0" - } - }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -5994,11 +5518,6 @@ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=" }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, "lodash.pairs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash.pairs/-/lodash.pairs-3.0.1.tgz", @@ -6012,23 +5531,6 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, - "log-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", - "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", - "requires": { - "chalk": "1.1.3" - } - }, - "log-update": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-1.0.2.tgz", - "integrity": "sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE=", - "requires": { - "ansi-escapes": "1.4.0", - "cli-cursor": "1.0.2" - } - }, "log-update-async-hook": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/log-update-async-hook/-/log-update-async-hook-2.0.2.tgz", @@ -6941,17 +6443,6 @@ } } }, - "ora": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", - "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", - "requires": { - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-spinners": "0.1.2", - "object-assign": "4.1.1" - } - }, "os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -7020,11 +6511,6 @@ "p-limit": "1.2.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==" - }, "p-some": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/p-some/-/p-some-2.0.1.tgz", @@ -7186,11 +6672,6 @@ "sha.js": "2.4.9" } }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -8037,11 +7518,6 @@ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=" }, - "ramda": { - "version": "0.24.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.24.1.tgz", - "integrity": "sha1-w7d1UZfzW43DUCIoJixMkd22uFc=" - }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -8369,14 +7845,6 @@ } } }, - "request-progress": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-0.3.1.tgz", - "integrity": "sha1-ByHBBdipasayzossia4tXs/Pazo=", - "requires": { - "throttleit": "0.0.2" - } - }, "requestidlecallback": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", @@ -8499,14 +7967,6 @@ "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=" }, - "rxjs": { - "version": "5.5.6", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.6.tgz", - "integrity": "sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg==", - "requires": { - "symbol-observable": "1.0.1" - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -9420,11 +8880,6 @@ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, - "stream-to-observable": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stream-to-observable/-/stream-to-observable-0.1.0.tgz", - "integrity": "sha1-Rb8dny19wJvtgfHDB8Qw5ouEz/4=" - }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -9611,11 +9066,6 @@ } } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=" - }, "table": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", @@ -10044,11 +9494,6 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=" - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -10438,11 +9883,6 @@ "imurmurhash": "0.1.4" } }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11216,15 +10656,6 @@ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" } } - }, - "yauzl": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.8.0.tgz", - "integrity": "sha1-eUUK/yKyqcWkHvVOAtuQfM+/nuI=", - "requires": { - "buffer-crc32": "0.2.13", - "fd-slicer": "1.0.1" - } } } } diff --git a/package.json b/package.json index 4913654..096582f 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,9 @@ "build-sass": "node ./bin/build-sass.js", "build-sass-watch": "node ./bin/build-sass.js --watch", "run-mastodon": "node ./bin/run-mastodon", - "cy:run": "cypress run", - "cy:open": "cypress open", + "run-testcafe": "testcafe --hostname localhost --skip-js-errors chrome:headless tests/spec", "test": "run-p --race run-mastodon dev test-mastodon", - "test-mastodon": "run-s wait-for-mastodon-to-start cy:run", + "test-mastodon": "run-s wait-for-mastodon-to-start run-testcafe", "wait-for-mastodon-to-start": "node bin/wait-for-mastodon-to-start.js", "globalize-css": "node ./bin/globalize-css.js", "deglobalize-css": "node ./bin/globalize-css.js --reverse", @@ -31,7 +30,6 @@ "compression": "^1.7.1", "cross-env": "^5.1.3", "css-loader": "^0.28.7", - "cypress": "^2.0.2", "express": "^4.16.2", "extract-text-webpack-plugin": "^3.0.2", "fg-loadcss": "^2.0.1", @@ -94,15 +92,8 @@ "__routes__", "__shell__", "__assets__", - "cy", - "Cypress", - "expect", - "before", - "beforeEach", - "after", - "afterEach", - "describe", - "it" + "test", + "fixture" ], "ignore": [ "dist", diff --git a/tests/spec/02-login-spec.js b/tests/spec/02-login-spec.js index a118f2f..9e21bb9 100644 --- a/tests/spec/02-login-spec.js +++ b/tests/spec/02-login-spec.js @@ -1,12 +1,25 @@ import { Selector as $ } from 'testcafe' import { addInstanceButton, getUrl, instanceInput, login, settingsButton } from '../utils' -import { foobarRole } from '../roles' fixture `02-login-spec.js` .page `http://localhost:4002` const formError = $('.form-error') +function manualLogin(t, username, password) { + return t.click($('a').withText('log in to an instance')) + .expect(getUrl()).contains('/settings/instances/add') + .typeText(instanceInput, 'localhost:3000') + .click(addInstanceButton) + .expect(getUrl()).eql('http://localhost:3000/auth/sign_in') + .typeText($('input#user_email'), username) + .typeText($('input#user_password'), password) + .click($('button[type=submit]')) + .expect(getUrl()).contains('/oauth/authorize') + .click($('button[type=submit]:not(.negative)')) + .expect(getUrl()).eql('http://localhost:4002/') +} + test('Cannot log in to a fake instance', async t => { await t.click($('a').withText('log in to an instance')) .expect(getUrl()).contains('/settings/instances/add') @@ -22,12 +35,12 @@ test('Cannot log in to a fake instance', async t => { }) test('Logs in to localhost:3000', async t => { - await t.useRole(foobarRole) + await manualLogin(t, 'foobar@localhost:3000', 'foobarfoobar') .expect($('article.status-article').exists).ok() }) test('Logs out', async t => { - await t.useRole(foobarRole) + await manualLogin(t, 'foobar@localhost:3000', 'foobarfoobar') .click(settingsButton) .click($('a').withText('Instances')) .click($('a').withText('localhost:3000')) diff --git a/tests/spec/04-pinned-statuses.js b/tests/spec/04-pinned-statuses.js new file mode 100644 index 0000000..9f36c56 --- /dev/null +++ b/tests/spec/04-pinned-statuses.js @@ -0,0 +1,35 @@ +import { Selector as $ } from 'testcafe' +import { getUrl, login } from '../utils' +import { foobarRole } from '../roles' + +fixture `04-pinned-statuses.js` + .page `http://localhost:4002` + +test("shows a user's pinned statuses", async t => { + await t.useRole(foobarRole) + .click($('nav a[aria-label=Community]')) + .expect(getUrl()).contains('/community') + .click($('a').withText(('Pinned'))) + .expect(getUrl()).contains('/pinned') + .expect($('.status-article').getAttribute('aria-posinset')).eql('0') + .expect($('.status-article').getAttribute('aria-setsize')).eql('1') + .expect($('.status-article .status-content').innerText).contains('this is unlisted') +}) + +test("shows pinned statuses on a user's account page", async t => { + await t.useRole(foobarRole) + .navigateTo('/accounts/2') + .expect($('.pinned-statuses .status-article').getAttribute('aria-posinset')).eql('0') + .expect($('.pinned-statuses .status-article').getAttribute('aria-setsize')).eql('1') + .expect($('.pinned-statuses .status-article').innerText).contains('this is unlisted') +}) + +test("shows pinned statuses on a user's account page 2", async t => { + await t.useRole(foobarRole) + .navigateTo('/accounts/3') + .expect($('.pinned-statuses .status-article').getAttribute('aria-posinset')).eql('0') + .expect($('.pinned-statuses .status-article').getAttribute('aria-setsize')).eql('2') + .expect($('.pinned-statuses .status-article').innerText).contains('pinned toot 1') + .expect($('.pinned-statuses .status-article[aria-posinset="1"]').getAttribute('aria-setsize')).eql('2') + .expect($('.pinned-statuses .status-article[aria-posinset="1"]').innerText).contains('pinned toot 2') +}) \ No newline at end of file diff --git a/tests/spec/05-status-types.js b/tests/spec/05-status-types.js new file mode 100644 index 0000000..5d49ea0 --- /dev/null +++ b/tests/spec/05-status-types.js @@ -0,0 +1,44 @@ +import { getNthVirtualArticle } from '../utils' +import { foobarRole } from '../roles' + +fixture `05-status-types.js` + .page `http://localhost:4002` + +test('shows direct vs followers-only vs regular', async t => { + await t.useRole(foobarRole) + .expect(getNthVirtualArticle(1).getAttribute('aria-label')).eql('Status by admin') + .expect(getNthVirtualArticle(1).find('.status-content').innerText).contains('notification of unlisted message') + .expect(getNthVirtualArticle(1).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Boost') + .expect(getNthVirtualArticle(1).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).notOk() + .expect(getNthVirtualArticle(2).getAttribute('aria-label')).eql('Status by admin') + .expect(getNthVirtualArticle(2).find('.status-content').innerText).contains('notification of followers-only message') + .expect(getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Cannot be boosted because this is followers-only') + .expect(getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).ok() + .expect(getNthVirtualArticle(3).getAttribute('aria-label')).eql('Direct message by admin') + .expect(getNthVirtualArticle(3).find('.status-content').innerText).contains('notification of direct message') + .expect(getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Cannot be boosted because this is a direct message') + .expect(getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).ok() +}) + +test('shows direct vs followers-only vs regular in notifications', async t => { + await t.useRole(foobarRole) + .navigateTo('/notifications') + .expect(getNthVirtualArticle(2).getAttribute('aria-label')).eql('Status by admin') + .expect(getNthVirtualArticle(2).find('.status-content').innerText).contains('notification of unlisted message') + .expect(getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Boost') + .expect(getNthVirtualArticle(2).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).notOk() + .expect(getNthVirtualArticle(3).getAttribute('aria-label')).eql('Status by admin') + .expect(getNthVirtualArticle(3).find('.status-content').innerText).contains('notification of followers-only message') + .expect(getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Cannot be boosted because this is followers-only') + .expect(getNthVirtualArticle(3).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).ok() + .expect(getNthVirtualArticle(4).getAttribute('aria-label')).eql('Direct message by admin') + .expect(getNthVirtualArticle(4).find('.status-content').innerText).contains('notification of direct message') + .expect(getNthVirtualArticle(4).find('.status-toolbar button:nth-child(2)').getAttribute('aria-label')) + .eql('Cannot be boosted because this is a direct message') + .expect(getNthVirtualArticle(4).find('.status-toolbar button:nth-child(2)').hasAttribute('disabled')).ok() +}) diff --git a/tests/spec/06-tabindex.js b/tests/spec/06-tabindex.js new file mode 100644 index 0000000..9ca7431 --- /dev/null +++ b/tests/spec/06-tabindex.js @@ -0,0 +1,39 @@ +import { Selector as $ } from 'testcafe' +import { getNthVirtualArticle } from '../utils' +import { foobarRole } from '../roles' + +fixture `06-tabindex.js` + .page `http://localhost:4002` + +test('shows correct tabindex in home timeline', async t => { + await t.useRole(foobarRole) + .expect(getNthVirtualArticle(0).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(1).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(2).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(3).getAttribute('tabindex')).eql('0') +}) + +test('shows correct tabindex in notifications', async t => { + await t.useRole(foobarRole) + .navigateTo('/notifications') + .expect(getNthVirtualArticle(0).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(1).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(2).getAttribute('tabindex')).eql('0') + .hover(getNthVirtualArticle(2)) + .expect(getNthVirtualArticle(3).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(4).getAttribute('tabindex')).eql('0') + .hover(getNthVirtualArticle(4)) + .expect(getNthVirtualArticle(5).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(6).getAttribute('tabindex')).eql('0') + .hover(getNthVirtualArticle(6)) + .expect(getNthVirtualArticle(7).getAttribute('tabindex')).eql('0') + .expect(getNthVirtualArticle(7).getAttribute('aria-setsize')).eql('8') +}) + +test('shows correct tabindex in pinned statuses', async t => { + await t.useRole(foobarRole) + .navigateTo('/pinned') + .expect($('.status-article').getAttribute('tabindex')).eql('0') + .expect($('.status-article').getAttribute('aria-posinset')).eql('0') + .expect($('.status-article').getAttribute('aria-setsize')).eql('1') +}) diff --git a/tests/spec/07-account-profile.js b/tests/spec/07-account-profile.js new file mode 100644 index 0000000..dc176d0 --- /dev/null +++ b/tests/spec/07-account-profile.js @@ -0,0 +1,39 @@ +import { Selector as $ } from 'testcafe' +import { getUrl } from '../utils' +import { foobarRole } from '../roles' + +fixture `07-account-profile.js` + .page `http://localhost:4002` + +test('shows account profile', async t => { + await t.useRole(foobarRole) + .click($('.status-author-name').withText(('quux'))) + .expect(getUrl()).contains('/accounts/3') + .expect($('.account-profile .account-profile-name').innerText).contains('quux') + .expect($('.account-profile .account-profile-username').innerText).contains('@quux') + .expect($('.account-profile .account-profile-followed-by').innerText).match(/follows you/i) + .expect($('.account-profile .account-profile-follow button').getAttribute('aria-label')).eql('Follow') + .expect($('.account-profile .account-profile-follow button').getAttribute('aria-pressed')).eql('false') +}) + +test('shows account profile 2', async t => { + await t.useRole(foobarRole) + .click($('.status-author-name').withText(('admin'))) + .expect(getUrl()).contains('/accounts/1') + .expect($('.account-profile .account-profile-name').innerText).contains('admin') + .expect($('.account-profile .account-profile-username').innerText).contains('@admin') + .expect($('.account-profile .account-profile-followed-by').innerText).match(/follows you/i) + .expect($('.account-profile .account-profile-follow button').getAttribute('aria-label')).eql('Unfollow') + .expect($('.account-profile .account-profile-follow button').getAttribute('aria-pressed')).eql('true') +}) + +test('shows account profile 3', async t => { + await t.useRole(foobarRole) + .click($('.mention').withText(('foobar'))) + .expect(getUrl()).contains('/accounts/2') + .expect($('.account-profile .account-profile-name').innerText).contains('foobar') + .expect($('.account-profile .account-profile-username').innerText).contains('@foobar') + // can't follow or be followed by your own account + .expect($('.account-profile .account-profile-followed-by').innerText).eql('') + .expect($('.account-profile .account-profile-follow').innerText).eql('') +})