diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000..e69de29 diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000..0ff2d64 --- /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" +} diff --git a/cypress/integration/console.test.js b/cypress/integration/console.test.js new file mode 100644 index 0000000..239d5d1 --- /dev/null +++ b/cypress/integration/console.test.js @@ -0,0 +1,64 @@ +/// + +const consoleAssert = (prompt, expected) => { + cy.get('#consolePromptEl').type(`${prompt}{enter}`); + + // append the prompt at the beginning of the expected items + expected.unshift(`"> ${prompt}"`); + + cy.get('ul[data-testid=consoleItems] li').should( + 'have.length', + expected.length + ); + + expected.forEach((expectedValue, index) => { + cy.get('ul[data-testid=consoleItems] li').eq(index).contains(expectedValue); + }); +}; + +describe('Console checks', () => { + beforeEach(() => { + cy.init(); + + cy.get('a[data-testid=toggleConsole]').click(); + }); + + it('Simple arithmetic addition', () => { + consoleAssert('4+5', ['9']); + }); + + it('Simple arithmetic subtraction', () => { + consoleAssert('4-5', ['-1']); + }); + + it('Simple arithmetic multiplication', () => { + consoleAssert('4*5', ['20']); + }); + + it('Simple arithmetic division', () => { + consoleAssert('4/5', ['0.8']); + }); + + it('Division by Zero', () => { + consoleAssert('4/0', ['Infinity']); + }); + + it('Console log a message', () => { + consoleAssert("console.log('hello')", ['hello', 'undefined']); + }); + + it('Equality check', () => { + consoleAssert("100 == '100'", ['true']); + }); + + it('Strict-Equality check', () => { + consoleAssert("100 === '100'", ['false']); + }); + + it.only('Minimizing console', () => { + cy.get('#consoleEl').should('not.have.class', 'is-minimized'); + cy.wait(200); // wait for animation to complete + cy.get('a[data-testid=toggleConsole]').click(); + cy.get('#consoleEl').should('have.class', 'is-minimized'); + }); +}); diff --git a/cypress/integration/layouts.test.js b/cypress/integration/layouts.test.js new file mode 100644 index 0000000..26e295d --- /dev/null +++ b/cypress/integration/layouts.test.js @@ -0,0 +1,94 @@ +/// + +describe('Pressing the layout buttons should change the layouts accordingly', () => { + before(() => { + cy.init(); + cy.viewport(1270, 720); + }); + + const getLayoutBtnId = index => `#layoutBtn${index}`; + + it('Default Layout', () => { + cy.get('body').should('have.class', 'layout-1'); + + cy.get('#js-code-side').should('be.visible'); + cy.get('#js-demo-side').should('be.visible'); + + cy.get('#js-code-side').should( + 'have.attr', + 'style', + 'width: calc(50% - 3px);' + ); + cy.get('#js-code-side').should('have.attr', 'direction', 'vertical'); + + cy.get('#js-demo-side').should( + 'have.attr', + 'style', + 'width: calc(50% - 3px);' + ); + }); + + it('Layout 2', () => { + cy.get(getLayoutBtnId(2)).click(); + + cy.get('body').should('have.class', 'layout-2'); + + cy.get('#js-code-side').should('be.visible'); + cy.get('#js-demo-side').should('be.visible'); + + cy.get('#js-code-side').should( + 'have.attr', + 'style', + 'height: calc(50% - 3px);' + ); + cy.get('#js-code-side').should('have.attr', 'direction', 'horizontal'); + + cy.get('#js-demo-side').should( + 'have.attr', + 'style', + 'height: calc(50% - 3px);' + ); + }); + + it('Layout 3', () => { + cy.get(getLayoutBtnId(3)).click(); + + cy.get('body').should('have.class', 'layout-3'); + + cy.get('#js-code-side').should('be.visible'); + cy.get('#js-demo-side').should('be.visible'); + + cy.get('#js-code-side').should( + 'have.attr', + 'style', + 'width: calc(50% - 3px);' + ); + cy.get('#js-code-side').should('have.attr', 'direction', 'vertical'); + + cy.get('#js-demo-side').should( + 'have.attr', + 'style', + 'width: calc(50% - 3px);' + ); + }); + + it('Layout 4', () => { + cy.get(getLayoutBtnId(4)).click(); + + cy.get('body').should('have.class', 'layout-4'); + + cy.get('#js-code-side').should('not.be.visible'); + cy.get('#js-demo-side').should('be.visible'); + }); + + it('Layout 5', () => { + cy.get(getLayoutBtnId(5)).click(); + + cy.get('body').should('have.class', 'layout-5'); + + cy.get('#js-code-side').should('be.visible'); + cy.get('#js-demo-side').should('be.visible'); + + cy.get('#js-code-side').should('have.attr', 'direction', 'horizontal'); + }); +}); diff --git a/cypress/integration/modals.test.js b/cypress/integration/modals.test.js new file mode 100644 index 0000000..9ec4193 --- /dev/null +++ b/cypress/integration/modals.test.js @@ -0,0 +1,27 @@ +/// + +describe('Modals pop-up when header btns are pressed', () => { + beforeEach(() => { + cy.init(); + }); + + // Selectors for each button + const ADD_LIBRARY_SEL = '[data-event-action=addLibraryButtonClick]'; + const NEW_SEL = '[aria-label="Start a new creation"]'; + const LOGIN_SEL = '[data-event-action=loginButtonClick]'; + + it('Add Library', () => { + cy.get(ADD_LIBRARY_SEL).click(); + cy.get('.modal__content').should('be.visible'); + }); + + it('+ New', () => { + cy.get(NEW_SEL).click(); + cy.get('.modal__content').should('be.visible'); + }); + + it('Login/SignUp', () => { + cy.get(LOGIN_SEL).click(); + cy.get('.modal__content').should('be.visible'); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000..e2696f9 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,22 @@ +/// +// *********************************************************** +// 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) + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +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 0000000..c20cc46 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,31 @@ +// *********************************************** +// 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) => { ... }) +Cypress.Commands.add('init', () => { + cy.visit('http://localhost:8080'); + + // closing the Welcome modal + cy.get('button[aria-label="Close modal"]').click(); +}); +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000..37a498f --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// 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/cypress/videos/console.test.js.mp4 b/cypress/videos/console.test.js.mp4 new file mode 100644 index 0000000..661b617 Binary files /dev/null and b/cypress/videos/console.test.js.mp4 differ diff --git a/cypress/videos/layouts.test.js.mp4 b/cypress/videos/layouts.test.js.mp4 new file mode 100644 index 0000000..26c8894 Binary files /dev/null and b/cypress/videos/layouts.test.js.mp4 differ diff --git a/cypress/videos/modals.test.js.mp4 b/cypress/videos/modals.test.js.mp4 new file mode 100644 index 0000000..b27395a Binary files /dev/null and b/cypress/videos/modals.test.js.mp4 differ diff --git a/package.json b/package.json index bede02b..9c3ae1f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "add-locale": "lingui add-locale", "extract": "lingui extract", "compile": "lingui compile", - "release:dev": "gulp devRelease" + "release:dev": "gulp devRelease", + "cypress:open": "cypress open", + "cypress": "cypress run" }, "eslintConfig": { "extends": "eslint-config-synacor" @@ -42,6 +44,7 @@ "babel-minify": "^0.2.0", "babel-plugin-macros": "^2.6.1", "concurrently": "^7.0.0", + "cypress": "^9.5.3", "eslint": "^4.9.0", "eslint-config-prettier": "^2.3.0", "eslint-config-synacor": "^2.0.2", diff --git a/src/components/Console.jsx b/src/components/Console.jsx index 6929d25..85d5172 100644 --- a/src/components/Console.jsx +++ b/src/components/Console.jsx @@ -119,6 +119,7 @@ export class Console extends PureComponent { class="code-wrap__header-btn code-wrap__collapse-btn" title={i18n._(t`Toggle console`)} onClick={toggleConsole} + data-testid="toggleConsole" /> @@ -127,6 +128,7 @@ export class Console extends PureComponent { ref={el => { this.logContainerEl = el; }} + data-testid="consoleItems" > {logs.map(log => (