diff --git a/.devcontainer/docker-compose.extend.yml b/.devcontainer/docker-compose.extend.yml index 20f83d0039..3493a18ffa 100644 --- a/.devcontainer/docker-compose.extend.yml +++ b/.devcontainer/docker-compose.extend.yml @@ -4,7 +4,8 @@ services: app: environment: EDITOR_VSCODE: 1 + DJANGO_SETTINGS_MODULE: settings_local_sqlitetest volumes: - - ..:/root/src:cached + - ..:/root/src # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. network_mode: service:db \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2afbc6d42c..84b6f07fed 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ /unix.tag *.pyc __pycache__ +node_modules diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9ab2bd71eb..7680b79cbe 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -43,7 +43,7 @@ "problemMatcher": [] }, { - "label": "Run JS Tests", + "label": "Run JS Tests (python)", "type": "shell", "command": "/usr/local/bin/python", "args": [ @@ -117,7 +117,7 @@ "type": "shell", "command": "/bin/bash", "args": [ - "${workspaceFolder}/docker/app-win32-timezone-fix.sh" + "${workspaceFolder}/docker/scripts/app-win32-timezone-fix.sh" ], "presentation": { "echo": true, @@ -128,6 +128,24 @@ "clear": false }, "problemMatcher": [] + }, + { + "label": "Run JS Tests (cypress)", + "type": "shell", + "command": "/bin/bash", + "args": [ + "${workspaceFolder}/docker/scripts/app-cypress.sh" + ], + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "new", + "showReuseMessage": false, + "clear": false + }, + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000000..b6c7bc09c4 --- /dev/null +++ b/cypress.json @@ -0,0 +1,6 @@ +{ + "baseUrl": "http://localhost:8000", + "chromeWebSecurity": false, + "viewportWidth": 1280, + "viewportHeight": 800 +} diff --git a/cypress/.gitignore b/cypress/.gitignore new file mode 100644 index 0000000000..c93c160370 --- /dev/null +++ b/cypress/.gitignore @@ -0,0 +1,2 @@ +screenshots +videos diff --git a/cypress/fixtures/users.json b/cypress/fixtures/users.json new file mode 100644 index 0000000000..79b699aa77 --- /dev/null +++ b/cypress/fixtures/users.json @@ -0,0 +1,232 @@ +[ + { + "id": 1, + "name": "Leanne Graham", + "username": "Bret", + "email": "Sincere@april.biz", + "address": { + "street": "Kulas Light", + "suite": "Apt. 556", + "city": "Gwenborough", + "zipcode": "92998-3874", + "geo": { + "lat": "-37.3159", + "lng": "81.1496" + } + }, + "phone": "1-770-736-8031 x56442", + "website": "hildegard.org", + "company": { + "name": "Romaguera-Crona", + "catchPhrase": "Multi-layered client-server neural-net", + "bs": "harness real-time e-markets" + } + }, + { + "id": 2, + "name": "Ervin Howell", + "username": "Antonette", + "email": "Shanna@melissa.tv", + "address": { + "street": "Victor Plains", + "suite": "Suite 879", + "city": "Wisokyburgh", + "zipcode": "90566-7771", + "geo": { + "lat": "-43.9509", + "lng": "-34.4618" + } + }, + "phone": "010-692-6593 x09125", + "website": "anastasia.net", + "company": { + "name": "Deckow-Crist", + "catchPhrase": "Proactive didactic contingency", + "bs": "synergize scalable supply-chains" + } + }, + { + "id": 3, + "name": "Clementine Bauch", + "username": "Samantha", + "email": "Nathan@yesenia.net", + "address": { + "street": "Douglas Extension", + "suite": "Suite 847", + "city": "McKenziehaven", + "zipcode": "59590-4157", + "geo": { + "lat": "-68.6102", + "lng": "-47.0653" + } + }, + "phone": "1-463-123-4447", + "website": "ramiro.info", + "company": { + "name": "Romaguera-Jacobson", + "catchPhrase": "Face to face bifurcated interface", + "bs": "e-enable strategic applications" + } + }, + { + "id": 4, + "name": "Patricia Lebsack", + "username": "Karianne", + "email": "Julianne.OConner@kory.org", + "address": { + "street": "Hoeger Mall", + "suite": "Apt. 692", + "city": "South Elvis", + "zipcode": "53919-4257", + "geo": { + "lat": "29.4572", + "lng": "-164.2990" + } + }, + "phone": "493-170-9623 x156", + "website": "kale.biz", + "company": { + "name": "Robel-Corkery", + "catchPhrase": "Multi-tiered zero tolerance productivity", + "bs": "transition cutting-edge web services" + } + }, + { + "id": 5, + "name": "Chelsey Dietrich", + "username": "Kamren", + "email": "Lucio_Hettinger@annie.ca", + "address": { + "street": "Skiles Walks", + "suite": "Suite 351", + "city": "Roscoeview", + "zipcode": "33263", + "geo": { + "lat": "-31.8129", + "lng": "62.5342" + } + }, + "phone": "(254)954-1289", + "website": "demarco.info", + "company": { + "name": "Keebler LLC", + "catchPhrase": "User-centric fault-tolerant solution", + "bs": "revolutionize end-to-end systems" + } + }, + { + "id": 6, + "name": "Mrs. Dennis Schulist", + "username": "Leopoldo_Corkery", + "email": "Karley_Dach@jasper.info", + "address": { + "street": "Norberto Crossing", + "suite": "Apt. 950", + "city": "South Christy", + "zipcode": "23505-1337", + "geo": { + "lat": "-71.4197", + "lng": "71.7478" + } + }, + "phone": "1-477-935-8478 x6430", + "website": "ola.org", + "company": { + "name": "Considine-Lockman", + "catchPhrase": "Synchronised bottom-line interface", + "bs": "e-enable innovative applications" + } + }, + { + "id": 7, + "name": "Kurtis Weissnat", + "username": "Elwyn.Skiles", + "email": "Telly.Hoeger@billy.biz", + "address": { + "street": "Rex Trail", + "suite": "Suite 280", + "city": "Howemouth", + "zipcode": "58804-1099", + "geo": { + "lat": "24.8918", + "lng": "21.8984" + } + }, + "phone": "210.067.6132", + "website": "elvis.io", + "company": { + "name": "Johns Group", + "catchPhrase": "Configurable multimedia task-force", + "bs": "generate enterprise e-tailers" + } + }, + { + "id": 8, + "name": "Nicholas Runolfsdottir V", + "username": "Maxime_Nienow", + "email": "Sherwood@rosamond.me", + "address": { + "street": "Ellsworth Summit", + "suite": "Suite 729", + "city": "Aliyaview", + "zipcode": "45169", + "geo": { + "lat": "-14.3990", + "lng": "-120.7677" + } + }, + "phone": "586.493.6943 x140", + "website": "jacynthe.com", + "company": { + "name": "Abernathy Group", + "catchPhrase": "Implemented secondary concept", + "bs": "e-enable extensible e-tailers" + } + }, + { + "id": 9, + "name": "Glenna Reichert", + "username": "Delphine", + "email": "Chaim_McDermott@dana.io", + "address": { + "street": "Dayna Park", + "suite": "Suite 449", + "city": "Bartholomebury", + "zipcode": "76495-3109", + "geo": { + "lat": "24.6463", + "lng": "-168.8889" + } + }, + "phone": "(775)976-6794 x41206", + "website": "conrad.com", + "company": { + "name": "Yost and Sons", + "catchPhrase": "Switchable contextually-based project", + "bs": "aggregate real-time technologies" + } + }, + { + "id": 10, + "name": "Clementina DuBuque", + "username": "Moriah.Stanton", + "email": "Rey.Padberg@karina.biz", + "address": { + "street": "Kattie Turnpike", + "suite": "Suite 198", + "city": "Lebsackbury", + "zipcode": "31428-2261", + "geo": { + "lat": "-38.2386", + "lng": "57.2232" + } + }, + "phone": "024-648-3804", + "website": "ambrose.net", + "company": { + "name": "Hoeger LLC", + "catchPhrase": "Centralized empowering task-force", + "bs": "target end-to-end models" + } + } +] \ No newline at end of file diff --git a/cypress/integration/meeting/agenda.spec.js b/cypress/integration/meeting/agenda.spec.js new file mode 100644 index 0000000000..92dd079203 --- /dev/null +++ b/cypress/integration/meeting/agenda.spec.js @@ -0,0 +1,103 @@ +/// + +describe.skip('meeting agenda', () => { + before(() => { + cy.visit('/meeting/agenda/') + }) + + it('toggle customize panel when clicking on customize header bar', () => { + cy.get('#agenda-filter-customize').click() + cy.get('#customize').should('be.visible').and('have.class', 'in') + + cy.get('#agenda-filter-customize').click() + cy.get('#customize').should('not.be.visible').and('not.have.class', 'in') + }) + + it('customize panel should have at least 3 areas', () => { + cy.get('#agenda-filter-customize').click() + cy.get('.agenda-filter-areaselectbtn').should('have.length.at.least', 3) + }) + + it('customize panel should have at least 10 groups', () => { + cy.get('.agenda-filter-groupselectbtn').should('have.length.at.least', 10) + }) + + it('filtering the agenda should modify the URL', () => { + // cy.intercept({ + // method: 'GET', + // path: '/meeting/agenda/week-view.html**', + // times: 10 + // }, { + // forceNetworkError: true + // }) + + cy.get('.agenda-filter-groupselectbtn').any(5).as('selectedGroups').each(randomElement => { + cy.wrap(randomElement).click() + cy.wrap(randomElement).invoke('attr', 'data-filter-item').then(keyword => { + cy.url().should('contain', keyword) + }) + }) + + // Deselect everything + cy.get('@selectedGroups').click({ multiple: true }) + }) + + it('selecting an area should select all corresponding groups', () => { + cy.get('.agenda-filter-areaselectbtn').any().click().invoke('attr', 'data-filter-item').then(area => { + cy.url().should('contain', area) + + cy.get(`.agenda-filter-groupselectbtn[data-filter-keywords*="${area}"]`).each(group => { + cy.wrap(group).invoke('attr', 'data-filter-keywords').then(groupKeywords => { + // In case value is a comma-separated list of keywords... + if (groupKeywords.indexOf(',') < 0 || groupKeywords.split(',').includes(area)) { + cy.wrap(group).should('have.class', 'active') + } + }) + }) + }) + }) + + it('weekview iframe should load', () => { + cy.get('iframe#weekview').its('0.contentDocument').should('exist') + cy.get('iframe#weekview').its('0.contentDocument.readyState').should('equal', 'complete') + cy.get('iframe#weekview').its('0.contentDocument.body', { + timeout: 30000 + }).should('not.be.empty') + }) +}) + +describe('meeting agenda weekview', () => { + before(() => { + cy.visit('/meeting/agenda/week-view.html') + }) + it('should have day headers', () => { + cy.get('.agenda-weekview-day').should('have.length.greaterThan', 0).and('be.visible') + }) + it('should have day columns', () => { + cy.get('.agenda-weekview-column').should('have.length.greaterThan', 0).and('be.visible') + }) + + it('should have the same number of day headers and columns', () => { + cy.get('.agenda-weekview-day').its('length').then(lgth => { + cy.get('.agenda-weekview-column').should('have.length', lgth) + }) + }) + + it('should have meetings', () => { + cy.get('.agenda-weekview-meeting').should('have.length.greaterThan', 0).and('be.visible') + }) + + it('meeting hover should cause expansion to column width', () => { + cy.get('.agenda-weekview-column:first').invoke('outerWidth').then(colWidth => { + cy.get('.agenda-weekview-meeting-mini').any(5).each(meeting => { + cy.wrap(meeting) + .wait(250) + .realHover({ position: 'center' }) + .invoke('outerWidth') + .should('be.closeTo', colWidth, 1) + // Move over to top left corner of the page to end the mouseover of the current meeting block + cy.get('.agenda-weekview-day:first').realHover().wait(250) + }) + }) + }) +}) \ No newline at end of file diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 0000000000..59b2bab6e4 --- /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 0000000000..3cc5bffab1 --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,34 @@ +// *********************************************** +// 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 will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) + +Cypress.Commands.add('any', { prevSubject: 'element' }, (subject, size = 1) => { + cy.wrap(subject).then(elementList => { + elementList = (elementList.jquery) ? elementList.get() : elementList + elementList = Cypress._.sampleSize(elementList, size) + elementList = (elementList.length > 1) ? elementList : elementList[0] + cy.wrap(elementList) + }) +}) \ No newline at end of file diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 0000000000..33bd59c13a --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,22 @@ +// *********************************************************** +// 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') + +import 'cypress-real-events/support' diff --git a/docker/app.Dockerfile b/docker/app.Dockerfile index 162fbf671a..af5a0e6081 100644 --- a/docker/app.Dockerfile +++ b/docker/app.Dockerfile @@ -35,6 +35,15 @@ RUN apt-get install -qy \ graphviz \ jq \ less \ + libgtk2.0-0 \ + libgtk-3-0 \ + libnotify-dev \ + libgconf-2-4 \ + libgbm-dev \ + libnss3 \ + libxss1 \ + libasound2 \ + libxtst6 \ libmagic-dev \ libmariadb-dev \ locales \ @@ -50,7 +59,9 @@ RUN apt-get install -qy \ subversion \ unzip \ wget \ - yang-tools && \ + xauth \ + xvfb \ + yang-tools \ zsh # Install chromedriver @@ -68,6 +79,15 @@ RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key # Get rid of installation files we don't need in the image, to reduce size RUN apt-get clean && rm -rf /var/lib/apt/lists/* +# "fake" dbus address to prevent errors +# https://github.com/SeleniumHQ/docker-selenium/issues/87 +ENV DBUS_SESSION_BUS_ADDRESS=/dev/null + +# avoid million NPM install messages +ENV npm_config_loglevel warn +# allow installing when the main user is root +ENV npm_config_unsafe_perm true + # Set locale to en_US.UTF-8 RUN echo "LC_ALL=en_US.UTF-8" >> /etc/environment && \ echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index dd664d019d..74b4613e3b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -23,6 +23,8 @@ services: depends_on: - db + ipc: host + # environment: # USER: django # UID: 1001 diff --git a/docker/scripts/app-cypress.sh b/docker/scripts/app-cypress.sh new file mode 100755 index 0000000000..79e4519145 --- /dev/null +++ b/docker/scripts/app-cypress.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +WORKSPACEDIR="/root/src" + +pushd . +cd $WORKSPACEDIR +echo "Installing NPM dependencies..." +npm install --silent + +echo "Starting datatracker server..." +ietf/manage.py runserver 0.0.0.0:8000 --settings=settings_local > /dev/null 2>&1 & +serverPID=$! + +echo "Waiting for server to come online ..." +wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.1.3/wait-for | sh -s -- localhost:8000 -- echo "Server ready" + +echo "Run dbus process to silence warnings..." +sudo mkdir -p /run/dbus +sudo dbus-daemon --system &> /dev/null + +echo "Starting JS tests..." +npx cypress run + +kill $serverPID +popd \ No newline at end of file diff --git a/ietf/templates/meeting/agenda.html b/ietf/templates/meeting/agenda.html index 0f3725a5cf..c4eac23a02 100644 --- a/ietf/templates/meeting/agenda.html +++ b/ietf/templates/meeting/agenda.html @@ -417,7 +417,7 @@

var new_url = 'week-view.html' + queryparams; var wv_iframe = document.getElementById('weekview'); var wv_window = wv_iframe.contentWindow; - if (wv_iframe.src && wv_window.history && wv_window.history.replaceState) { + if (wv_iframe.src && wv_window.location.hostname && wv_window.history && wv_window.history.replaceState) { wv_window.history.replaceState({}, '', new_url); wv_window.redraw_weekview(); } else { diff --git a/ietf/templates/meeting/agenda_filter.html b/ietf/templates/meeting/agenda_filter.html index dff07a03a0..8c06aedd41 100644 --- a/ietf/templates/meeting/agenda_filter.html +++ b/ietf/templates/meeting/agenda_filter.html @@ -12,7 +12,7 @@ {% if not always_show %}

- + {% firstof customize_button_text "Customize..."%}

@@ -46,7 +46,7 @@

{% for area in fc %} {% if area.keyword %} - @@ -65,7 +65,7 @@

{% for button in p.children %}
-