add_npm_command(
OUTPUT "${CMAKE_SOURCE_DIR}/src/pybind/mgr/dashboard/frontend/node_modules"
- COMMAND NG_CLI_ANALYTICS=false npm ci
+ COMMAND NG_CLI_ANALYTICS=false CYPRESS_CACHE_FOLDER=${CMAKE_SOURCE_DIR}/build/src/pybind/mgr/dashboard/cypress npm ci
DEPENDS frontend/package.json
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/pybind/mgr/dashboard/frontend
COMMENT "dashboard frontend dependencies are being installed"
Running End-to-End (E2E) Tests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We use `Protractor <http://www.protractortest.org/>`__ to run our frontend E2E
-tests.
+We use `Cypress <https://www.cypress.io/>`__ to run our frontend E2E tests.
-Our ``run-frontend-e2e-tests.sh`` script will check if Chrome or Docker is
-installed and run the tests if either is found.
+E2E Prerequisites
+.................
+
+You need to previously build the frontend.
+
+In some environments, depending on your user permissions and the CYPRESS_CACHE_FOLDER,
+you might need to run ``npm ci`` with the ``--unsafe-perm`` flag.
+
+You might need to install additional packages to be able to run Cypress.
+Please run ``npx cypress verify`` to verify it.
+
+run-frontend-e2e-tests.sh
+.........................
+
+Our ``run-frontend-e2e-tests.sh`` script is the go to solution when you wish to
+do a full scale e2e run.
+It will verify if everything needed is installed, start a new vstart cluster
+and run the full test suite.
Start all frontend E2E tests by running::
$ ./run-frontend-e2e-tests.sh
Report:
- After running the tests you can find the corresponding report as well as screenshots
- of failed test cases by opening the following file in your browser:
+ You can follow the e2e report on the terminal and you can find the screenshots
+ of failed test cases by opening the following directory::
- src/pybind/mgr/dashboard/frontend/.protractor-report/index.html
+ src/pybind/mgr/dashboard/frontend/cypress/screenshots/
Device:
You can force the script to use a specific device with the ``-d`` flag::
- $ ./run-frontend-e2e-tests.sh -d <chrome|docker>
+ $ ./run-frontend-e2e-tests.sh -d <chrome|chromium|electron|docker>
Remote:
+ By default this script will stop and start a new vstart cluster.
If you want to run the tests outside the ceph environment, you will need to
- manually define the dashboard url using ``-r`` and, optionally, credentials (``-u``, ``-p``)::
+ manually define the dashboard url using ``-r`` and, optionally, credentials
+ (``-u``, ``-p``)::
$ ./run-frontend-e2e-tests.sh -r <DASHBOARD_URL> -u <E2E_LOGIN_USER> -p <E2E_LOGIN_PWD>
When using docker, as your device, you might need to run the script with sudo
permissions.
-When developing E2E tests, it is not necessary to compile the frontend code
-on each change of the test files. When your development environment is
-running (``npm start``), you can point Protractor to just use this
-environment. To attach `Protractor <http://www.protractortest.org/>`__ to
-this process, run ``npm run e2e:ci``.
+Other running options
+.....................
-Note::
+During active development, it is not recommended to run the previous script,
+as it is not prepared for constant file changes.
+Instead you should use one of the following commands:
+
+- ``npm run e2e`` - This will run ``ng serve`` and open the Cypress Test Runner.
+- ``npm run e2e:ci`` - This will run ``ng serve`` and run the Cypress Test Runner once.
+- ``npx cypress run`` - This calls cypress directly and will run the Cypress Test Runner.
+ You need to have a running frontend server.
+- ``npx cypress open`` - This calls cypress directly and will open the Cypress Test Runner.
+ You need to have a running frontend server.
+
+Calling Cypress directly has the advantage that you can use any of the available
+`flags <https://docs.cypress.io/guides/guides/command-line.html#cypress-run>`__
+to customize your test run and you don't need to start a frontend server each time.
+
+Using one of the ``open`` commands, will open a cypress application where you
+can see all the test files you have and run each individually.
+This is going to be run in watch mode, so if you make any changes to test files,
+it will retrigger the test run.
+This cannot be used inside docker, as it requires X11 environment to be able to open.
+
+By default Cypress will look for the web page at ``https://localhost:4200/``.
+If you are serving it in a different URL you will need to configure it by
+exporting the environment variable CYPRESS_BASE_URL with the new value.
+E.g.: ``CYPRESS_BASE_URL=https://localhost:41076/ npx cypress open``
+
+CYPRESS_CACHE_FOLDER
+.....................
- In case you have a somewhat particular environment, you might need to adapt
- `protractor.conf.js` to point to the appropriate destination.
+When installing cypress via npm, a binary of the cypress app will also be
+downloaded and stored in a cache folder.
+This removes the need to download it every time you run ``npm ci`` or even when
+using cypress in a separate project.
-Writing End-to-End Tests
-~~~~~~~~~~~~~~~~~~~~~~~~
+By default Cypress uses ~/.cache to store the binary.
+To prevent changes to the user home directory, we have changed this folder to
+``/ceph/build/src/pybind/mgr/dashboard/cypress``, so when you build ceph or run
+``run-frontend-e2e-tests.sh`` this is the directory Cypress will use.
-To be used methods
-..................
+When using any other command to install or run cypress,
+it will go back to the default directory. It is recommended that you export the
+CYPRESS_CACHE_FOLDER environment variable with a fixed directory, so you always
+use the same directory no matter which command you use.
-For clicking checkboxes, the ``clickCheckbox`` method is supposed to be used.
-Due an adaption of the ``<input type="checkbox">`` tag, the original checkbox
-is hidden and unclickable. Instead, a fancier replacement is shown. When the
-developer tries to use `ElementFinder::click()` on such a checkbox, it will
-raise an error. The ``clickCheckbox`` method prevents that by clicking the
-label of the checkbox, like a regular user would do.
+
+Writing End-to-End Tests
+~~~~~~~~~~~~~~~~~~~~~~~~
The PagerHelper class
.....................
Examples are
-- ``getTableCellByContent()`` - returns a table cell by its content
+- ``navigateTo()`` - Navigates to a specific page and waits for it to load
+- ``getFirstTableCell()`` - returns the first table cell. You can also pass a
+ string with the desired content and it will return the first cell that
+ contains it.
- ``getTabsCount()`` - returns the amount of tabs
-- ``clickCheckbox()`` - clicks a checkbox
Every method that could be useful on several pages belongs there. Also, methods
which enhance the derived classes of the PageHelper belong there. A good
and ``delete()``. These methods are specific to a pool but are useful for other
suites.
-Methods that return HTML elements (for instance of type ``ElementFinder`` or
-``ElementArrayFinder``, but also ``Promise<ElementFinder>``) which can only
-be found on a specific page, should be either implemented in the helper
-methods of the subclass of PageHelper or as own methods of the subclass of
-PageHelper.
-
-Registering a new PageHelper
-""""""""""""""""""""""""""""
-
-If you have to create a new Helper class derived from the ``PageHelper``,
-please also ensure that it is instantiated in the constructor of the
-``Helper`` class. That way it can automatically be used by all other suites.
-
-.. code:: TypeScript
-
- class Helper {
- // ...
- pools: PoolPageHelper;
-
- constructor() {
- this.pools = new PoolPageHelper();
- }
-
- // ...
- }
+Methods that return HTML elements which can only be found on a specific page,
+should be either implemented in the helper methods of the subclass of PageHelper
+or as own methods of the subclass of PageHelper.
Using PageHelpers
"""""""""""""""""
-In any suite, an instance of the ``Helper`` class should be used to call
-various ``PageHelper`` objects and their methods. This makes all methods of all
-PageHelpers available to all suites.
+In any suite, an instance of the specific ``Helper`` class should be
+instantiated and called directly.
.. code:: TypeScript
+ const pools = new PoolPageHelper();
+
it('should create a pool', () => {
- helper.pools.exist(poolName, false).then(() => {
- helper.pools.navigateTo('create');
- helper.pools.create(poolName).then(() => {
- helper.pools.navigateTo();
- helper.pools.exist(poolName, true);
- });
- });
+ pools.exist(poolName, false);
+ pools.navigateTo('create');
+ pools.create(poolName, 8);
+ pools.exist(poolName, true);
});
Code Style
..........
-Please refer to the official `Protractor style-guide
-<https://www.protractortest.org/#/style-guide>`__ for a better insight on how
-to write and structure tests as well as what exactly should be covered by
-end-to-end tests.
+Please refer to the official `Cypress Core Concepts
+<https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Cypress-Can-Be-Simple-Sometimes>`__
+for a better insight on how to write and structure tests.
``describe()`` vs ``it()``
""""""""""""""""""""""""""
-Both ``describe()`` and ``it()`` are function blocks, meaning that any executable
-code necessary for the test can be contained in either block. However, Typescript
-scoping rules still apply, therefore any variables declared in a ``describe`` are available
-to the ``it()`` blocks inside of it.
+Both ``describe()`` and ``it()`` are function blocks, meaning that any
+executable code necessary for the test can be contained in either block.
+However, Typescript scoping rules still apply, therefore any variables declared
+in a ``describe`` are available to the ``it()`` blocks inside of it.
-``describe()`` typically are containers for tests, allowing you to break tests into
-multiple parts. Likewise, any setup that must be made before your tests are run can be
-initialized within the ``describe()`` block. Here is an example:
+``describe()`` typically are containers for tests, allowing you to break tests
+into multiple parts. Likewise, any setup that must be made before your tests are
+run can be initialized within the ``describe()`` block. Here is an example:
.. code:: TypeScript
describe('create, edit & delete image test', () => {
const poolName = 'e2e_images_pool';
- beforeAll(() => {
- pools.navigateTo('create'); // Need pool for image testing
- pools.create(poolName, 8, 'rbd').then(() => {
- pools.navigateTo();
- pools.exist(poolName, true);
- });
+ before(() => {
+ cy.login();
+ pools.navigateTo('create');
+ pools.create(poolName, 8, 'rbd');
+ pools.exist(poolName, true);
+ });
+
+ beforeEach(() => {
+ cy.login();
images.navigateTo();
});
+ //...
+
+ });
+
As shown, we can initiate the variable ``poolName`` as well as run commands
-before our test suite begins (creating a pool). ``describe()`` block messages should
-include what the test suite is.
+before our test suite begins (creating a pool). ``describe()`` block messages
+should include what the test suite is.
-``it()`` blocks typically are parts of an overarching test. They contain the functionality of
-the test suite, each performing individual roles. Here is an example:
+``it()`` blocks typically are parts of an overarching test. They contain the
+functionality of the test suite, each performing individual roles.
+Here is an example:
.. code:: TypeScript
- describe('create, edit & delete image test', () => {
- it('should create image', () => {
- images.createImage(imageName, poolName, '1');
- expect(images.getTableCell(imageName).isPresent()).toBe(true);
- });
- it('should edit image', () => {
- images.editImage(imageName, poolName, newImageName, '2');
- expect(images.getTableCell(newImageName).isPresent()).toBe(true);
+ describe('create, edit & delete image test', () => {
+ //...
+
+ it('should create image', () => {
+ images.createImage(imageName, poolName, '1');
+ images.getFirstTableCell(imageName).should('exist');
+ });
+
+ it('should edit image', () => {
+ images.editImage(imageName, poolName, newImageName, '2');
+ images.getFirstTableCell(newImageName).should('exist');
+ });
+
+ //...
});
- //...
- });
-As shown from the previous example, our ``describe()`` test suite is to create, edit
-and delete an image. Therefore, each ``it()`` completes one of these steps, one for creating,
-one for editing, and so on. Likewise, every ``it()`` blocks message should be in lowercase
-and written so long as "it" can be the prefix of the message. For example, ``it('edits the test image' () => ...)``
-vs. ``it('image edit test' () => ...)``. As shown, the first example makes grammatical sense with ``it()`` as the
-prefix whereas the second message does not.``it()`` should describe what the individual test is doing and
-what it expects to happen.
+As shown from the previous example, our ``describe()`` test suite is to create,
+edit and delete an image. Therefore, each ``it()`` completes one of these steps,
+one for creating, one for editing, and so on. Likewise, every ``it()`` blocks
+message should be in lowercase and written so long as "it" can be the prefix of
+the message. For example, ``it('edits the test image' () => ...)`` vs.
+``it('image edit test' () => ...)``. As shown, the first example makes
+grammatical sense with ``it()`` as the prefix whereas the second message does
+not. ``it()`` should describe what the individual test is doing and what it
+expects to happen.
Differences between Frontend Unit Tests and End-to-End (E2E) Tests / FAQ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E2E test:
-"Protractor is an end-to-end test framework for Angular and AngularJS applications.
-Protractor runs tests against your application running in a real browser,
-interacting with it as a user would." `(src) <http://www.protractortest.org/#/>`__
-
It requires a fully functional system and tests the interaction of all components
of the application (Ceph, back-end, front-end).
E2E tests are designed to mimic the behavior of the user when interacting with the application
/src/unit-test-configuration.ts
# e2e
-/e2e/*.js
-/e2e/*.map
-.protractor-fail-fast
+/cypress/screenshots
+/cypress/videos
# System Files
.DS_Store
}
},
"cli": {}
- },
- "ceph-dashboard-e2e": {
- "root": "",
- "sourceRoot": "",
- "projectType": "application",
- "architect": {
- "e2e": {
- "builder": "@angular-devkit/build-angular:protractor",
- "options": {
- "protractorConfig": "./protractor.conf.js",
- "devServerTarget": "ceph-dashboard:serve"
- }
- },
- "lint": {
- "builder": "@angular-devkit/build-angular:tslint",
- "options": {
- "tsConfig": [
- "e2e/tsconfig.e2e.json"
- ],
- "exclude": [
- "**/node_modules/**"
- ]
- }
- }
- },
- "cli": {}
}
},
"defaultProject": "ceph-dashboard",
--- /dev/null
+{
+ "baseUrl": "http://localhost:4200/",
+ "ignoreTestFiles": [
+ "*.po.ts"
+ ],
+ "supportFile": "cypress/support/index.ts",
+ "video": false,
+ "defaultCommandTimeout": 20000,
+ "viewportHeight": 1080,
+ "viewportWidth": 1920,
+ "pluginsFile": false,
+ "fixturesFolder": false,
+ "projectId": "k7ab29"
+}
--- /dev/null
+import { PoolPageHelper } from '../pools/pools.po';
+import { ImagesPageHelper } from './images.po';
+
+describe('Images page', () => {
+ const pools = new PoolPageHelper();
+ const images = new ImagesPageHelper();
+
+ const poolName = 'e2e_images_pool';
+
+ before(() => {
+ cy.login();
+ // Need pool for image testing
+ pools.navigateTo('create');
+ pools.create(poolName, 8, 'rbd');
+ pools.exist(poolName, true);
+ });
+
+ after(() => {
+ // Deletes images test pool
+ pools.navigateTo();
+ pools.delete(poolName);
+ pools.navigateTo();
+ pools.exist(poolName, false);
+ });
+
+ beforeEach(() => {
+ cy.login();
+ images.navigateTo();
+ });
+
+ it('should open and show breadcrumb', () => {
+ images.expectBreadcrumbText('Images');
+ });
+
+ it('should show four tabs', () => {
+ images.getTabsCount().should('eq', 4);
+ });
+
+ it('should show text for all tabs', () => {
+ images.getTabText(0).should('eq', 'Images');
+ images.getTabText(1).should('eq', 'Namespaces');
+ images.getTabText(2).should('eq', 'Trash');
+ images.getTabText(3).should('eq', 'Overall Performance');
+ });
+
+ describe('create, edit & delete image test', () => {
+ const imageName = 'e2e_images#image';
+ const newImageName = 'e2e_images#image_new';
+
+ it('should create image', () => {
+ images.createImage(imageName, poolName, '1');
+ images.getFirstTableCell(imageName).should('exist');
+ });
+
+ it('should edit image', () => {
+ images.editImage(imageName, poolName, newImageName, '2');
+ images.getFirstTableCell(newImageName).should('exist');
+ });
+
+ it('should delete image', () => {
+ images.delete(newImageName);
+ });
+ });
+
+ describe('move to trash, restore and purge image tests', () => {
+ const imageName = 'e2e_trash#image';
+ const newImageName = 'e2e_newtrash#image';
+
+ before(() => {
+ cy.login();
+ // Need image for trash testing
+ images.createImage(imageName, poolName, '1');
+ images.getFirstTableCell(imageName).should('exist');
+ });
+
+ it('should move the image to the trash', () => {
+ images.moveToTrash(imageName);
+ images.getFirstTableCell(imageName).should('exist');
+ });
+
+ it('should restore image to images table', () => {
+ images.restoreImage(imageName, newImageName);
+ images.getFirstTableCell(newImageName).should('exist');
+ });
+
+ it('should purge trash in images trash tab', () => {
+ images.getFirstTableCell(newImageName).should('exist');
+ images.moveToTrash(newImageName);
+ images.purgeTrash(newImageName, poolName);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class ImagesPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/block/rbd', id: 'cd-rbd-list' },
+ create: { url: '#/block/rbd/create', id: 'cd-rbd-form' }
+ };
+
+ // Creates a block image and fills in the name, pool, and size fields.
+ // Then checks if the image is present in the Images table.
+ createImage(name: string, pool: string, size: string) {
+ this.navigateTo('create');
+
+ cy.get('#name').type(name); // Enter in image name
+
+ // Select image pool
+ cy.contains('Loading...').should('not.exist');
+ this.selectOption('pool', pool);
+ cy.get('#pool').should('have.class', 'ng-valid'); // check if selected
+
+ // Enter in the size of the image
+ cy.get('#size').type(size);
+
+ // Click the create button and wait for image to be made
+ cy.contains('button', 'Create RBD').click();
+ this.getFirstTableCell(name).should('exist');
+ }
+
+ editImage(name: string, pool: string, newName: string, newSize: string) {
+ const base_url = '#/block/rbd/edit/';
+ const editURL = base_url
+ .concat(encodeURIComponent(pool))
+ .concat('%2F')
+ .concat(encodeURIComponent(name));
+ cy.visit(editURL);
+
+ // Wait until data is loaded
+ cy.get('#pool').should('contain.value', pool);
+
+ cy.get('#name').clear().type(newName);
+ cy.get('#size').clear().type(newSize); // click the size box and send new size
+
+ cy.contains('button', 'Edit RBD').click();
+
+ this.getExpandCollapseElement(newName).click();
+ cy.get('.table.table-striped.table-bordered').contains('td', newSize);
+ }
+
+ // Selects RBD image and moves it to the trash,
+ // checks that it is present in the trash table
+ moveToTrash(name: string) {
+ // wait for image to be created
+ cy.get('.datatable-body').first().should('not.contain.text', '(Creating...)');
+
+ this.getFirstTableCell(name).click();
+
+ // click on the drop down and selects the move to trash option
+ cy.get('.table-actions button.dropdown-toggle').first().click();
+ cy.get('li.move-to-trash').click();
+
+ cy.contains('button', 'Move Image').should('be.visible').click();
+
+ // Clicks trash tab
+ cy.contains('.nav-link', 'Trash').click();
+ this.getFirstTableCell(name).should('exist');
+ }
+
+ // Checks trash tab table for image and then restores it to the RBD Images table
+ // (could change name if new name is given)
+ restoreImage(name: string, newName?: string) {
+ // clicks on trash tab
+ cy.contains('.nav-link', 'Trash').click();
+
+ // wait for table to load
+ this.getFirstTableCell(name).click();
+ cy.contains('button', 'Restore').click();
+
+ // wait for pop-up to be visible (checks for title of pop-up)
+ cy.get('#name').should('be.visible');
+
+ // If a new name for the image is passed, it changes the name of the image
+ if (newName !== undefined) {
+ // click name box and send new name
+ cy.get('#name').clear().type(newName);
+ }
+
+ cy.contains('button', 'Restore Image').click();
+
+ // clicks images tab
+ cy.contains('.nav-link', 'Images').click();
+
+ this.getFirstTableCell(newName).should('exist');
+ }
+
+ // Enters trash tab and purges trash, thus emptying the trash table.
+ // Checks if Image is still in the table.
+ purgeTrash(name: string, pool?: string) {
+ // clicks trash tab
+ cy.contains('.nav-link', 'Trash').click();
+ cy.contains('button', 'Purge Trash').click();
+
+ // Check for visibility of modal container
+ cy.get('.modal-header').should('be.visible');
+
+ // If purgeing a specific pool, selects that pool if given
+ if (pool !== undefined) {
+ this.selectOption('poolName', pool);
+ cy.get('#poolName').should('have.class', 'ng-valid'); // check if pool is selected
+ }
+ cy.get('#purgeFormButton').click();
+ // Wait for image to delete and check it is not present
+
+ this.getFirstTableCell(name).should('not.exist');
+ }
+}
--- /dev/null
+import { IscsiPageHelper } from './iscsi.po';
+
+describe('Iscsi Page', () => {
+ const iscsi = new IscsiPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ iscsi.navigateTo();
+ });
+
+ it('should open and show breadcrumb', () => {
+ iscsi.expectBreadcrumbText('Overview');
+ });
+
+ it('should check that tables are displayed and legends are correct', () => {
+ // Check tables are displayed
+ iscsi.getDataTables().its(0).should('be.visible');
+ iscsi.getDataTables().its(1).should('visible');
+
+ // Check that legends are correct
+ iscsi.getLegends().its(0).should('contain.text', 'Gateways');
+ iscsi.getLegends().its(1).should('contain.text', 'Images');
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class IscsiPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/block/iscsi/overview', id: 'cd-iscsi' }
+ };
+}
--- /dev/null
+import { PoolPageHelper } from '../pools/pools.po';
+import { MirroringPageHelper } from './mirroring.po';
+
+describe('Mirroring page', () => {
+ const pools = new PoolPageHelper();
+ const mirroring = new MirroringPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ mirroring.navigateTo();
+ });
+
+ it('should open and show breadcrumb', () => {
+ mirroring.expectBreadcrumbText('Mirroring');
+ });
+
+ it('should show three tabs', () => {
+ mirroring.getTabsCount().should('eq', 3);
+ });
+
+ it('should show text for all tabs', () => {
+ mirroring.getTabText(0).should('eq', 'Issues');
+ mirroring.getTabText(1).should('eq', 'Syncing');
+ mirroring.getTabText(2).should('eq', 'Ready');
+ });
+
+ describe('checks that edit mode functionality shows in the pools table', () => {
+ const poolName = 'mirroring_test';
+
+ beforeEach(() => {
+ pools.navigateTo('create'); // Need pool for mirroring testing
+ pools.create(poolName, 8, 'rbd');
+ pools.navigateTo();
+ pools.exist(poolName, true);
+ });
+
+ it('tests editing mode for pools', () => {
+ mirroring.navigateTo();
+
+ mirroring.editMirror(poolName, 'Pool');
+ mirroring.getFirstTableCell('pool').should('be.visible');
+ mirroring.editMirror(poolName, 'Image');
+ mirroring.getFirstTableCell('image').should('be.visible');
+ mirroring.editMirror(poolName, 'Disabled');
+ mirroring.getFirstTableCell('disabled').should('be.visible');
+ });
+
+ afterEach(() => {
+ pools.navigateTo();
+ pools.delete(poolName);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/block/mirroring', id: 'cd-mirroring' }
+};
+
+export class MirroringPageHelper extends PageHelper {
+ pages = pages;
+
+ /**
+ * Goes to the mirroring page and edits a pool in the Pool table. Clicks on the
+ * pool and chooses an option (either pool, image, or disabled)
+ */
+ @PageHelper.restrictTo(pages.index.url)
+ editMirror(name: string, option: string) {
+ // Clicks the pool in the table
+ this.getFirstTableCell(name).click();
+
+ // Clicks the Edit Mode button
+ cy.contains('button', 'Edit Mode').click();
+
+ // Clicks the drop down in the edit pop-up, then clicks the Update button
+ cy.get('.modal-content').should('be.visible');
+ this.selectOption('mirrorMode', option);
+
+ // Clicks update button and checks if the mode has been changed
+ cy.contains('button', 'Update').click();
+ cy.contains('.modal-dialog', 'Edit pool mirror mode').should('not.exist');
+ const val = option.toLowerCase(); // used since entries in table are lower case
+ this.getFirstTableCell(val).should('be.visible');
+ }
+}
--- /dev/null
+import { ConfigurationPageHelper } from './configuration.po';
+
+describe('Configuration page', () => {
+ const configuration = new ConfigurationPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ configuration.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ configuration.expectBreadcrumbText('Configuration');
+ });
+ });
+
+ describe('fields check', () => {
+ beforeEach(() => {
+ configuration.getExpandCollapseElement().click();
+ });
+
+ it('should verify that selected footer increases when an entry is clicked', () => {
+ configuration.getTableSelectedCount().should('eq', 1);
+ });
+
+ it('should check that details table opens and tab is correct', () => {
+ configuration.getStatusTables().should('be.visible');
+ configuration.getTabsCount().should('eq', 1);
+ configuration.getTabText(0).should('eq', 'Details');
+ });
+ });
+
+ describe('edit configuration test', () => {
+ const configName = 'client_cache_size';
+
+ beforeEach(() => {
+ configuration.clearTableSearchInput();
+ });
+
+ after(() => {
+ configuration.configClear(configName);
+ });
+
+ it('should click and edit a configuration and results should appear in the table', () => {
+ configuration.edit(
+ configName,
+ ['global', '1'],
+ ['mon', '2'],
+ ['mgr', '3'],
+ ['osd', '4'],
+ ['mds', '5'],
+ ['client', '6']
+ );
+ });
+
+ it('should show only modified configurations', () => {
+ configuration.filterTable('Modified', 'yes');
+ configuration.getTableFoundCount().should('eq', 1);
+ });
+
+ it('should hide all modified configurations', () => {
+ configuration.filterTable('Modified', 'no');
+ configuration.getTableFoundCount().should('gt', 1);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class ConfigurationPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/configuration', id: 'cd-configuration' }
+ };
+
+ /**
+ * Clears out all the values in a config to reset before and after testing
+ * Does not work for configs with checkbox only, possible future PR
+ */
+ configClear(name: string) {
+ this.navigateTo();
+ const valList = ['global', 'mon', 'mgr', 'osd', 'mds', 'client']; // Editable values
+
+ // Enter config setting name into filter box
+ this.seachTable(name);
+
+ // Selects config that we want to clear
+ this.getFirstTableCell(name).click(); // waits for config to be clickable and click
+ cy.contains('button', 'Edit').click(); // clicks button to edit
+
+ // Wait for the data to load
+ cy.contains('.card-header', `Edit ${name}`);
+
+ for (const i of valList) {
+ cy.get(`#${i}`).clear();
+ }
+ // Clicks save button and checks that values are not present for the selected config
+ cy.contains('button', 'Save').click();
+
+ // Enter config setting name into filter box
+ this.seachTable(name);
+
+ // Expand row
+ this.getExpandCollapseElement(name).click();
+
+ // Checks for visibility of details tab
+ this.getStatusTables().should('be.visible');
+
+ for (const i of valList) {
+ // Waits until values are not present in the details table
+ this.getStatusTables().should('not.contain.text', i + ':');
+ }
+ }
+
+ /**
+ * Clicks the designated config, then inputs the values passed into the edit function.
+ * Then checks if the edit is reflected in the config table.
+ * Takes in name of config and a list of tuples of values the user wants edited,
+ * each tuple having the desired value along with the number tehey want for that value.
+ * Ex: [global, '2'] is the global value with an input of 2
+ */
+ edit(name: string, ...values: [string, string][]) {
+
+ // Enter config setting name into filter box
+ this.seachTable(name);
+
+ // Selects config that we want to edit
+ this.getFirstTableCell(name).click(); // waits for config to be clickable and click
+ cy.contains('button', 'Edit').click(); // clicks button to edit
+
+ cy.contains('.card-header', `Edit ${name}`);
+
+ values.forEach((valtuple) => {
+ // Finds desired value based off given list
+ cy.get(`#${valtuple[0]}`).type(valtuple[1]); // of values and inserts the given number for the value
+ });
+
+ // Clicks save button then waits until the desired config is visible, clicks it,
+ // then checks that each desired value appears with the desired number
+ cy.contains('button', 'Save').click();
+
+ // Enter config setting name into filter box
+ this.seachTable(name);
+
+ // Checks for visibility of config in table
+ this.getExpandCollapseElement(name).should('be.visible').click();
+
+ // Clicks config
+ values.forEach((value) => {
+ // iterates through list of values and
+ // checks if the value appears in details with the correct number attatched
+ cy.contains('.table.table-striped.table-bordered', `${value[0]}\: ${value[1]}`);
+ });
+ }
+}
--- /dev/null
+import { CrushMapPageHelper } from './crush-map.po';
+
+describe('CRUSH map page', () => {
+ const crushmap = new CrushMapPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ crushmap.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ crushmap.expectBreadcrumbText('CRUSH map');
+ });
+ });
+
+ describe('fields check', () => {
+ it('should check that title & table appears', () => {
+ // Check that title (CRUSH map viewer) appears
+ crushmap.getPageTitle().should('equal', 'CRUSH map viewer');
+
+ // Check that title appears once OSD is clicked
+ crushmap.getCrushNode(1).click();
+
+ crushmap
+ .getLegends()
+ .invoke('text')
+ .then((legend) => {
+ crushmap.getCrushNode(1).should('have.text', legend);
+ });
+
+ // Check that table appears once OSD is clicked
+ crushmap.getDataTables().should('be.visible');
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class CrushMapPageHelper extends PageHelper {
+ pages = { index: { url: '#/crush-map', id: 'cd-crushmap' } };
+
+ getPageTitle() {
+ return cy.get('cd-crushmap .card-header').text();
+ }
+
+ getCrushNode(idx: number) {
+ return cy.get('.node-name.type-osd').eq(idx);
+ }
+}
--- /dev/null
+import { HostsPageHelper } from './hosts.po';
+
+describe('Hosts page', () => {
+ const hosts = new HostsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ hosts.navigateTo();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ it('should open and show breadcrumb', () => {
+ hosts.expectBreadcrumbText('Hosts');
+ });
+
+ it('should show two tabs', () => {
+ hosts.getTabsCount().should('eq', 2);
+ });
+
+ it('should show hosts list tab at first', () => {
+ hosts.getTabText(0).should('eq', 'Hosts List');
+ });
+
+ it('should show overall performance as a second tab', () => {
+ hosts.getTabText(1).should('eq', 'Overall Performance');
+ });
+ });
+
+ describe('services link test', () => {
+ it('should check at least one host is present', () => {
+ hosts.check_for_host();
+ });
+
+ it('should check services link(s) work for first host', () => {
+ hosts.check_services_links();
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class HostsPageHelper extends PageHelper {
+ pages = { index: { url: '#/hosts', id: 'cd-hosts' } };
+
+ check_for_host() {
+ this.getTableTotalCount().should('not.be.eq', 0);
+ }
+
+ // function that checks all services links work for first
+ // host in table
+ check_services_links() {
+ // check that text (links) is present in services box
+ let links_tested = 0;
+
+ cy.get('cd-hosts a.service-link')
+ .should('have.length.greaterThan', 0)
+ .then(($elems) => {
+ $elems.each((_i, $el) => {
+ // click link, check it worked by looking for changed breadcrumb,
+ // navigate back to hosts page, repeat until all links checked
+ cy.contains('a', $el.innerText).should('exist').click();
+ this.expectBreadcrumbText('Performance Counters');
+ this.navigateTo();
+ links_tested++;
+ });
+ // check if any links were actually tested
+ expect(links_tested).gt(0);
+ });
+ }
+}
--- /dev/null
+import { PoolPageHelper } from '../pools/pools.po';
+import { ConfigurationPageHelper } from './configuration.po';
+import { LogsPageHelper } from './logs.po';
+
+describe('Logs page', () => {
+ const logs = new LogsPageHelper();
+ const pools = new PoolPageHelper();
+ const configuration = new ConfigurationPageHelper();
+
+ const poolname = 'e2e_logs_test_pool';
+ const configname = 'log_graylog_port';
+ const today = new Date();
+ let hour = today.getHours();
+ if (hour > 12) {
+ hour = hour - 12;
+ }
+ const minute = today.getMinutes();
+
+ beforeEach(() => {
+ cy.login();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ beforeEach(() => {
+ logs.navigateTo();
+ });
+
+ it('should open and show breadcrumb', () => {
+ logs.expectBreadcrumbText('Logs');
+ });
+
+ it('should show two tabs', () => {
+ logs.getTabsCount().should('eq', 2);
+ });
+
+ it('should show cluster logs tab at first', () => {
+ logs.getTabText(0).should('eq', 'Cluster Logs');
+ });
+
+ it('should show audit logs as a second tab', () => {
+ logs.getTabText(1).should('eq', 'Audit Logs');
+ });
+ });
+
+ describe('audit logs respond to pool creation and deletion test', () => {
+ it('should create pool and check audit logs reacted', () => {
+ pools.navigateTo('create');
+ pools.create(poolname, 8);
+ pools.navigateTo();
+ pools.exist(poolname, true);
+ logs.checkAuditForPoolFunction(poolname, 'create', hour, minute);
+ });
+
+ it('should delete pool and check audit logs reacted', () => {
+ pools.navigateTo();
+ pools.delete(poolname);
+ logs.checkAuditForPoolFunction(poolname, 'delete', hour, minute);
+ });
+ });
+
+ describe('audit logs respond to editing configuration setting test', () => {
+ it('should change config settings and check audit logs reacted', () => {
+ configuration.navigateTo();
+ configuration.edit(configname, ['global', '5']);
+
+ logs.navigateTo();
+ logs.checkAuditForConfigChange(configname, 'global', hour, minute);
+
+ configuration.navigateTo();
+ configuration.configClear(configname);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class LogsPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/logs', id: 'cd-logs' }
+ };
+
+ checkAuditForPoolFunction(poolname: string, poolfunction: string, hour: number, minute: number) {
+ this.navigateTo();
+
+ // sometimes the modal from deleting pool is still present at this point.
+ // This wait makes sure it isn't
+ cy.contains('.modal-dialog', 'Delete Pool').should('not.exist');
+
+ // go to audit logs tab
+ cy.contains('.nav-link', 'Audit Logs').click();
+
+ // Enter an earliest time so that no old messages with the same pool name show up
+ cy.get('.bs-timepicker-field').its(0).clear();
+
+ if (hour < 10) {
+ cy.get('.bs-timepicker-field').its(0).type('0');
+ }
+ cy.get('.bs-timepicker-field').its(0).type(`${hour}`);
+
+ cy.get('.bs-timepicker-field').its(1).clear();
+ if (minute < 10) {
+ cy.get('.bs-timepicker-field').its(1).type('0');
+ }
+ cy.get('.bs-timepicker-field').its(1).type(`${minute}`);
+
+ // Enter the pool name into the filter box
+ cy.get('input.form-control.ng-valid').first().clear().type(poolname);
+
+ cy.get('.tab-pane.active')
+ .get('.card-body')
+ .get('.message')
+ .should('contain.text', poolname)
+ .and('contain.text', `pool ${poolfunction}`);
+ }
+
+ checkAuditForConfigChange(configname: string, setting: string, hour: number, minute: number) {
+ this.navigateTo();
+
+ // go to audit logs tab
+ cy.contains('.nav-link', 'Audit Logs').click();
+
+ // Enter an earliest time so that no old messages with the same config name show up
+ cy.get('.bs-timepicker-field').its(0).clear();
+ if (hour < 10) {
+ cy.get('.bs-timepicker-field').its(0).type('0');
+ }
+ cy.get('.bs-timepicker-field').its(0).type(`${hour}`);
+
+ cy.get('.bs-timepicker-field').its(1).clear();
+ if (minute < 10) {
+ cy.get('.bs-timepicker-field').its(1).type('0');
+ }
+ cy.get('.bs-timepicker-field').its(1).type(`${minute}`);
+
+ // Enter the config name into the filter box
+ cy.get('input.form-control.ng-valid').first().clear().type(configname);
+
+ cy.get('.tab-pane.active')
+ .get('.card-body')
+ .get('.message')
+ .should('contain.text', configname)
+ .and('contain.text', setting);
+ }
+}
--- /dev/null
+import { ManagerModulesPageHelper } from './mgr-modules.po';
+
+describe('Manager modules page', () => {
+ const mgrmodules = new ManagerModulesPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ mgrmodules.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ mgrmodules.expectBreadcrumbText('Manager modules');
+ });
+ });
+
+ describe('verifies editing functionality for manager modules', () => {
+ it('should test editing on diskprediction_local module', () => {
+ const diskpredLocalArr = [
+ ['11', 'predict_interval'],
+ ['0122', 'sleep_interval']
+ ];
+ mgrmodules.editMgrModule('diskprediction_local', diskpredLocalArr);
+ });
+
+ it('should test editing on balancer module', () => {
+ const balancerArr = [['rq', 'pool_ids']];
+ mgrmodules.editMgrModule('balancer', balancerArr);
+ });
+
+ it('should test editing on dashboard module', () => {
+ const dashboardArr = [
+ ['rq', 'RGW_API_USER_ID'],
+ ['rafa', 'GRAFANA_API_PASSWORD']
+ ];
+ mgrmodules.editMgrModule('dashboard', dashboardArr);
+ });
+
+ it('should test editing on devicehealth module', () => {
+ mgrmodules.editDevicehealth('1987', 'sox', '1999', '2020', '456', '567');
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class ManagerModulesPageHelper extends PageHelper {
+ pages = { index: { url: '#/mgr-modules', id: 'cd-mgr-module-list' } };
+
+ /**
+ * Selects the Manager Module and then fills in the desired fields.
+ * Doesn't check/uncheck boxes because it is not reflected in the details table.
+ * DOES NOT WORK FOR ALL MGR MODULES, for example, Device health
+ */
+ editMgrModule(name: string, tuple: string[][]) {
+ this.getFirstTableCell(name).click();
+ cy.contains('button', 'Edit').click();
+
+ for (const entry of tuple) {
+ // Clears fields and adds edits
+ cy.get(`#${entry[1]}`).clear().type(entry[0]);
+ }
+
+ cy.contains('button', 'Update').click();
+ // Checks if edits appear
+ this.getExpandCollapseElement(name).should('be.visible').click();
+ for (const entry of tuple) {
+ cy.get('.datatable-body').last().contains(entry[0]);
+ }
+
+ // Clear mgr module of all edits made to it
+ this.getFirstTableCell(name).click();
+ cy.contains('button', 'Edit').click();
+
+ // Clears the editable fields
+ for (const entry of tuple) {
+ cy.get(`#${entry[1]}`).clear();
+ }
+
+ // Checks that clearing represents in details tab of module
+ cy.contains('button', 'Update').click();
+ this.getExpandCollapseElement(name).should('be.visible').click();
+ for (const entry of tuple) {
+ cy.get('.datatable-body').eq(1).should('contain', entry[1]).and('not.contain', entry[0]);
+ }
+ }
+
+ /**
+ * Selects the Devicehealth manager module, then fills in the desired fields,
+ * including all fields except checkboxes.
+ * Then checks if these edits appear in the details table.
+ */
+ editDevicehealth(
+ threshhold?: string,
+ pooln?: string,
+ retention?: string,
+ scrape?: string,
+ sleep?: string,
+ warn?: string
+ ) {
+ let devHealthArray: [string, string][];
+ devHealthArray = [
+ [threshhold, 'mark_out_threshold'],
+ [pooln, 'pool_name'],
+ [retention, 'retention_period'],
+ [scrape, 'scrape_frequency'],
+ [sleep, 'sleep_interval'],
+ [warn, 'warn_threshold']
+ ];
+
+ this.getFirstTableCell('devicehealth').click();
+ cy.contains('button', 'Edit').click();
+ for (let i = 0, devHealthTuple; (devHealthTuple = devHealthArray[i]); i++) {
+ if (devHealthTuple[0] !== undefined) {
+ // Clears and inputs edits
+ cy.get(`#${devHealthTuple[1]}`).type(devHealthTuple[0]);
+ }
+ }
+
+ cy.contains('button', 'Update').click();
+ this.getFirstTableCell('devicehealth').should('be.visible');
+ // Checks for visibility of devicehealth in table
+ this.getExpandCollapseElement('devicehealth').click();
+ for (let i = 0, devHealthTuple: [string, string]; (devHealthTuple = devHealthArray[i]); i++) {
+ if (devHealthTuple[0] !== undefined) {
+ // Repeatedly reclicks the module to check if edits has been done
+ cy.contains('.datatable-body-cell-label', 'devicehealth').click();
+ cy.get('.datatable-body').last().contains(devHealthTuple[0]).should('be.visible');
+ }
+ }
+
+ // Inputs old values into devicehealth fields. This manager module doesn't allow for updates
+ // to be made when the values are cleared. Therefore, I restored them to their original values
+ // (on my local run of ceph-dev, this is subject to change i would assume).
+ // I'd imagine there is a better way of doing this.
+ this.getFirstTableCell('devicehealth').click();
+ cy.contains('button', 'Edit').click();
+ cy.get('#mark_out_threshold').clear().type('2419200');
+
+ cy.get('#pool_name').clear().type('device_health_metrics');
+
+ cy.get('#retention_period').clear().type('15552000');
+
+ cy.get('#scrape_frequency').clear().type('86400');
+
+ cy.get('#sleep_interval').clear().type('600');
+
+ cy.get('#warn_threshold').clear().type('7257600');
+
+ // Checks that clearing represents in details tab
+ cy.contains('button', 'Update').click();
+ this.getExpandCollapseElement('devicehealth').should('be.visible').click();
+ for (let i = 0, devHealthTuple: [string, string]; (devHealthTuple = devHealthArray[i]); i++) {
+ if (devHealthTuple[0] !== undefined) {
+ // Repeatedly reclicks the module to check if clearing has been done
+ cy.contains('.datatable-body-cell-label', 'devicehealth').click();
+ cy.get('.datatable-body')
+ .eq(1)
+ .should('contain', devHealthTuple[1])
+ .and('not.contain', devHealthTuple[0]);
+ }
+ }
+ }
+}
--- /dev/null
+import { MonitorsPageHelper } from './monitors.po';
+
+describe('Monitors page', () => {
+ const monitors = new MonitorsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ monitors.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ monitors.expectBreadcrumbText('Monitors');
+ });
+ });
+
+ describe('fields check', () => {
+ it('should check status table is present', () => {
+ // check for table header 'Status'
+ monitors.getLegends().its(0).should('have.text', 'Status');
+
+ // check for fields in table
+ monitors
+ .getStatusTables()
+ .should('contain.text', 'Cluster ID')
+ .and('contain.text', 'monmap modified')
+ .and('contain.text', 'monmap epoch')
+ .and('contain.text', 'quorum con')
+ .and('contain.text', 'quorum mon')
+ .and('contain.text', 'required con')
+ .and('contain.text', 'required mon');
+ });
+
+ it('should check In Quorum and Not In Quorum tables are present', () => {
+ // check for there to be two tables
+ monitors.getDataTables().should('have.length', 2);
+
+ // check for table header 'In Quorum'
+ monitors.getLegends().its(1).should('have.text', 'In Quorum');
+
+ // check for table header 'Not In Quorum'
+ monitors.getLegends().its(2).should('have.text', 'Not In Quorum');
+
+ // verify correct columns on In Quorum table
+ monitors.getDataTableHeaders(0).contains('Name');
+
+ monitors.getDataTableHeaders(0).contains('Rank');
+
+ monitors.getDataTableHeaders(0).contains('Public Address');
+
+ monitors.getDataTableHeaders(0).contains('Open Sessions');
+
+ // verify correct columns on Not In Quorum table
+ monitors.getDataTableHeaders(1).contains('Name');
+
+ monitors.getDataTableHeaders(1).contains('Rank');
+
+ monitors.getDataTableHeaders(1).contains('Public Address');
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class MonitorsPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/monitor', id: 'cd-monitor' }
+ };
+}
--- /dev/null
+import { OSDsPageHelper } from './osds.po';
+
+describe('OSDs page', () => {
+ const osds = new OSDsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ osds.navigateTo();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ it('should open and show breadcrumb', () => {
+ osds.expectBreadcrumbText('OSDs');
+ });
+
+ it('should show two tabs', () => {
+ osds.getTabsCount().should('eq', 2);
+ osds.getTabText(0).should('eq', 'OSDs List');
+ osds.getTabText(1).should('eq', 'Overall Performance');
+ });
+ });
+
+ describe('check existence of fields on OSD page', () => {
+ it('should check that number of rows and count in footer match', () => {
+ osds.getTableTotalCount().then((text) => {
+ osds.getTableRows().its('length').should('equal', text);
+ });
+ });
+
+ it('should verify that buttons exist', () => {
+ cy.contains('button', 'Create');
+ cy.contains('button', 'Cluster-wide configuration');
+ });
+
+ describe('by selecting one row in OSDs List', () => {
+ beforeEach(() => {
+ osds.getExpandCollapseElement().click();
+ });
+
+ it('should verify that selected footer increases', () => {
+ osds.getTableSelectedCount().should('equal', 1);
+ });
+
+ it('should show the correct text for the tab labels', () => {
+ cy.get('#tabset-osd-details > div > tab').then(($tabs) => {
+ const tabHeadings = $tabs.map((_i, e) => e.getAttribute('heading')).get();
+
+ expect(tabHeadings).to.eql([
+ 'Devices',
+ 'Attributes (OSD map)',
+ 'Metadata',
+ 'Device health',
+ 'Performance counter',
+ 'Histogram',
+ 'Performance Details'
+ ]);
+ });
+ });
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class OSDsPageHelper extends PageHelper {
+ pages = { index: { url: '#/osd', id: 'cd-osd-list' } };
+}
--- /dev/null
+import { FilesystemsPageHelper } from './filesystems.po';
+
+describe('Filesystems page', () => {
+ const filesystems = new FilesystemsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ filesystems.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ filesystems.expectBreadcrumbText('Filesystems');
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class FilesystemsPageHelper extends PageHelper {
+ pages = { index: { url: '#/cephfs', id: 'cd-cephfs-list' } };
+}
--- /dev/null
+import { NfsPageHelper } from './nfs.po';
+
+describe('Nfs page', () => {
+ const nfs = new NfsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ nfs.navigateTo();
+ });
+
+ describe('breadcrumb test', () => {
+ it('should open and show breadcrumb', () => {
+ nfs.expectBreadcrumbText('NFS');
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class NfsPageHelper extends PageHelper {
+ pages = { index: { url: '#/nfs', id: 'cd-nfs-501' } };
+}
--- /dev/null
+interface Page {
+ url: string;
+ id: string;
+}
+
+export abstract class PageHelper {
+ pages: Record<string, Page>;
+
+ /**
+ * Decorator to be used on Helper methods to restrict access to one particular URL. This shall
+ * help developers to prevent and highlight mistakes. It also reduces boilerplate code and by
+ * thus, increases readability.
+ */
+ static restrictTo(page: string): Function {
+ return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
+ const fn: Function = descriptor.value;
+ descriptor.value = function (...args: any) {
+ cy.location('hash').should((url) => {
+ expect(url).to.eq(
+ page,
+ `Method ${target.constructor.name}::${propertyKey} is supposed to be ` +
+ `run on path "${page}", but was run on URL "${url}"`
+ );
+ });
+ fn.apply(this, args);
+ };
+ };
+ }
+
+ /**
+ * Navigates to the given page or to index.
+ * Waits until the page component is loaded
+ */
+ navigateTo(name: string = null) {
+ name = name || 'index';
+ const page = this.pages[name];
+
+ cy.visit(page.url);
+ cy.get(page.id);
+ }
+
+ /**
+ * Navigates back and waits for the hash to change
+ */
+ navigateBack() {
+ cy.location('hash').then((hash) => {
+ cy.go('back');
+ cy.location('hash').should('not.be', hash);
+ });
+ }
+
+ /**
+ * Checks the active breadcrumb value.
+ */
+ expectBreadcrumbText(text: string) {
+ cy.get('.breadcrumb-item.active').should('have.text', text);
+ }
+
+ getTabText(index: number) {
+ return cy.get('.nav.nav-tabs li').its(index).text();
+ }
+
+ getTabsCount(): any {
+ return cy.get('.nav.nav-tabs li').its('length');
+ }
+
+ /**
+ * Helper method to select an option inside a select element.
+ * This method will also expect that the option was set.
+ * @param option The option text (not value) to be selected.
+ */
+ selectOption(selectionName: string, option: string) {
+ cy.get(`select[name=${selectionName}]`).select(option);
+ return this.expectSelectOption(selectionName, option);
+ }
+
+ /**
+ * Helper method to expect a set option inside a select element.
+ * @param option The selected option text (not value) that is to
+ * be expected.
+ */
+ expectSelectOption(selectionName: string, option: string) {
+ return cy.get(`select[name=${selectionName}] option:checked`).contains(option);
+ }
+
+ getLegends() {
+ return cy.get('legend');
+ }
+
+ getToast() {
+ return cy.get('.ngx-toastr');
+ }
+
+ /**
+ * Waits for the table to load its data
+ * Should be used in all methods that access the datatable
+ */
+ private waitDataTableToLoad() {
+ cy.get('cd-table').should('exist');
+ cy.get('datatable-scroller, .empty-row');
+ }
+
+ getDataTables() {
+ this.waitDataTableToLoad();
+
+ return cy.get('cd-table .dataTables_wrapper');
+ }
+
+ getTableTotalCount() {
+ this.waitDataTableToLoad();
+
+ return cy.get('.datatable-footer-inner .page-count span').then(($elem) => {
+ const text = $elem
+ .filter((_i, e) => e.innerText.includes('total'))
+ .first()
+ .text();
+
+ return Number(text.match(/(\d+)\s+total/)[1]);
+ });
+ }
+
+ getTableSelectedCount() {
+ this.waitDataTableToLoad();
+
+ return cy.get('.datatable-footer-inner .page-count span').then(($elem) => {
+ const text = $elem
+ .filter((_i, e) => e.innerText.includes('selected'))
+ .first()
+ .text();
+
+ return Number(text.match(/(\d+)\s+selected/)[1]);
+ });
+ }
+
+ getTableFoundCount() {
+ this.waitDataTableToLoad();
+
+ return cy.get('.datatable-footer-inner .page-count span').then(($elem) => {
+ const text = $elem
+ .filter((_i, e) => e.innerText.includes('found'))
+ .first()
+ .text();
+
+ return Number(text.match(/(\d+)\s+found/)[1]);
+ });
+ }
+
+ getTableRow(content: string) {
+ this.waitDataTableToLoad();
+
+ this.seachTable(content);
+ return cy.contains('.datatable-body-row', content);
+ }
+
+ getTableRows() {
+ this.waitDataTableToLoad();
+
+ return cy.get('datatable-row-wrapper');
+ }
+
+ /**
+ * Returns the first table cell.
+ * Optionally, you can specify the content of the cell.
+ */
+ getFirstTableCell(content?: string) {
+ this.waitDataTableToLoad();
+
+ if (content) {
+ this.seachTable(content);
+ return cy.contains('.datatable-body-cell-label', content);
+ } else {
+ return cy.get('.datatable-body-cell-label').first();
+ }
+ }
+
+ getExpandCollapseElement(content?: string) {
+ this.waitDataTableToLoad();
+
+ if (content) {
+ return cy.contains('.datatable-body-row', content).find('.tc_expand-collapse');
+ } else {
+ return cy.get('.tc_expand-collapse').first();
+ }
+ }
+
+ /**
+ * Gets column headers of table
+ */
+ getDataTableHeaders(index = 0) {
+ this.waitDataTableToLoad();
+
+ return cy.get('.datatable-header').its(index).find('.datatable-header-cell-label');
+ }
+
+ /**
+ * Grabs striped tables
+ */
+ getStatusTables() {
+ return cy.get('.table.table-striped');
+ }
+
+ filterTable(name: string, option: string) {
+ this.waitDataTableToLoad();
+
+ cy.get('.tc_filter_name > a').click();
+ cy.contains(`.tc_filter_name .dropdown-item`, name).click();
+
+ cy.get('.tc_filter_option > a').click();
+ cy.contains(`.tc_filter_option .dropdown-item`, option).click();
+ }
+
+ seachTable(text: string) {
+ this.waitDataTableToLoad();
+
+ cy.get('cd-table .dataTables_paginate input').first().clear().type('10');
+ cy.get('cd-table .search input').first().clear().type(text);
+ }
+
+ clearTableSearchInput() {
+ this.waitDataTableToLoad();
+
+ return cy.get('cd-table .search button').click();
+ }
+
+ /**
+ * This is a generic method to delete table rows.
+ * It will select the first row that contains the provided name and delete it.
+ * After that it will wait until the row is no longer displayed.
+ */
+ delete(name: string) {
+ // Selects row
+ this.getFirstTableCell(name).click();
+
+ // Clicks on table Delete button
+ cy.get('.table-actions button.dropdown-toggle').first().click(); // open submenu
+ cy.get('li.delete a').click(); // click on "delete" menu item
+
+ // Confirms deletion
+ cy.get('.custom-control-label').click();
+ cy.contains('button', 'Delete').click();
+
+ // Wait for modal to close
+ cy.get('cd-modal').should('not.exist');
+
+ // Waits for item to be removed from table
+ this.getFirstTableCell(name).should('not.exist');
+ }
+}
--- /dev/null
+import { PoolPageHelper } from './pools.po';
+
+describe('Pools page', () => {
+ const pools = new PoolPageHelper();
+ const poolName = 'pool_e2e_pool/test';
+
+ beforeEach(() => {
+ cy.login();
+ pools.navigateTo();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ it('should open and show breadcrumb', () => {
+ pools.expectBreadcrumbText('Pools');
+ });
+
+ it('should show two tabs', () => {
+ pools.getTabsCount().should('equal', 2);
+ });
+
+ it('should show pools list tab at first', () => {
+ pools.getTabText(0).should('eq', 'Pools List');
+ });
+
+ it('should show overall performance as a second tab', () => {
+ pools.getTabText(1).should('eq', 'Overall Performance');
+ });
+ });
+
+ describe('Create, update and destroy', () => {
+ it('should create a pool', () => {
+ pools.exist(poolName, false);
+ pools.navigateTo('create');
+ pools.create(poolName, 8);
+ pools.exist(poolName, true);
+ });
+
+ it('should edit a pools placement group', () => {
+ pools.exist(poolName, true);
+ pools.edit_pool_pg(poolName, 32);
+ });
+
+ it('should delete a pool', () => {
+ pools.delete(poolName);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/pool', id: 'cd-pool-list' },
+ create: { url: '#/pool/create', id: 'cd-pool-form' }
+};
+
+export class PoolPageHelper extends PageHelper {
+ pages = pages;
+
+ private isPowerOf2(n: number) {
+ // tslint:disable-next-line: no-bitwise
+ return expect((n & (n - 1)) === 0, `Placement groups ${n} are not a power of 2`).to.be.true;
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ exist(name: string, oughtToBePresent = true) {
+ const waitRule = oughtToBePresent ? 'be.visible' : 'not.exist';
+ this.getFirstTableCell(name).should(waitRule);
+ }
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(name: string, placement_groups: number, ...apps: string[]) {
+ cy.get('input[name=name]').clear().type(name);
+
+ this.isPowerOf2(placement_groups);
+
+ this.selectOption('poolType', 'replicated');
+
+ this.expectSelectOption('pgAutoscaleMode', 'on');
+ this.selectOption('pgAutoscaleMode', 'off'); // To show pgNum field
+ cy.get('input[name=pgNum]').clear().type(`${placement_groups}`);
+ this.setApplications(apps);
+ cy.get('cd-submit-button').click();
+ }
+
+ edit_pool_pg(name: string, new_pg: number, wait = true) {
+ this.isPowerOf2(new_pg);
+ this.getFirstTableCell(name).click(); // select pool from the table
+ cy.contains('button', 'Edit').click(); // click edit button
+ this.expectBreadcrumbText('Edit'); // verify we are now on edit page
+ cy.get('input[name=pgNum]').clear().type(`${new_pg}`);
+ cy.get('cd-submit-button').click();
+ const str = `${new_pg} active+clean`;
+ this.getTableRow(name);
+ if (wait) {
+ this.getTableRow(name).contains(str);
+ }
+ }
+
+ private setApplications(apps: string[]) {
+ if (!apps || apps.length === 0) {
+ return;
+ }
+ cy.get('.float-left.mr-2.select-menu-edit').click();
+ cy.get('.popover-content.popover-body').should('be.visible');
+ apps.forEach((app) => cy.get('.select-menu-item-content').contains(app).click());
+ }
+}
--- /dev/null
+import { BucketsPageHelper } from './buckets.po';
+
+describe('RGW buckets page', () => {
+ const buckets = new BucketsPageHelper();
+ const bucket_name = 'e2ebucket';
+
+ beforeEach(() => {
+ cy.login();
+ buckets.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should open and show breadcrumb', () => {
+ buckets.expectBreadcrumbText('Buckets');
+ });
+ });
+
+ describe('create, edit & delete bucket tests', () => {
+ it('should create bucket', () => {
+ buckets.navigateTo('create');
+ buckets.create(
+ bucket_name,
+ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
+ 'default-placement'
+ );
+ buckets.getFirstTableCell(bucket_name).should('exist');
+ });
+
+ it('should edit bucket', () => {
+ buckets.edit(bucket_name, 'dev');
+ buckets.getDataTables().should('contain.text', 'dev');
+ });
+
+ it('should delete bucket', () => {
+ buckets.delete(bucket_name);
+ });
+ });
+
+ describe('Invalid Input in Create and Edit tests', () => {
+ it('should test invalid inputs in create fields', () => {
+ buckets.testInvalidCreate();
+ });
+
+ it('should test invalid input in edit owner field', () => {
+ buckets.navigateTo('create');
+ buckets.create(
+ bucket_name,
+ '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
+ 'default-placement'
+ );
+ buckets.testInvalidEdit(bucket_name);
+ buckets.navigateTo();
+ buckets.delete(bucket_name);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/rgw/bucket', id: 'cd-rgw-bucket-list' },
+ create: { url: '#/rgw/bucket/create', id: 'cd-rgw-bucket-form' }
+};
+
+export class BucketsPageHelper extends PageHelper {
+ pages = pages;
+
+ versioningStateEnabled = 'Enabled';
+ versioningStateSuspended = 'Suspended';
+
+ private selectOwner(owner: string) {
+ return this.selectOption('owner', owner);
+ }
+
+ private selectPlacementTarget(placementTarget: string) {
+ return this.selectOption('placement-target', placementTarget);
+ }
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(name: string, owner: string, placementTarget: string) {
+ // Enter in bucket name
+ cy.get('#bid').type(name);
+
+ // Select bucket owner
+ this.selectOwner(owner);
+ cy.get('#owner').should('have.class', 'ng-valid');
+
+ // Select bucket placement target:
+ this.selectPlacementTarget(placementTarget);
+ cy.get('#placement-target').should('have.class', 'ng-valid');
+
+ // Click the create button and wait for bucket to be made
+ cy.contains('button', 'Create Bucket').click();
+
+ this.getFirstTableCell(name).should('exist');
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ edit(name: string, new_owner: string) {
+ this.getFirstTableCell(name).click(); // wait for table to load and click
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+ this.expectBreadcrumbText('Edit');
+ cy.get('input[name=placement-target]').should('have.value', 'default-placement');
+ this.selectOwner(new_owner);
+
+ // Enable versioning
+ cy.get('input[id=versioning]').should('not.be.checked');
+ cy.get('label[for=versioning]').click();
+ cy.get('input[id=versioning]').should('be.checked');
+
+ cy.contains('button', 'Edit Bucket').click();
+
+ // wait to be back on buckets page with table visible and click
+ this.getExpandCollapseElement(name).click();
+
+ // check its details table for edited owner field
+ cy.get('.table.table-striped.table-bordered')
+ .first()
+ .should('contains.text', new_owner)
+ .as('bucketDataTable');
+
+ // Check versioning enabled:
+ cy.get('@bucketDataTable').find('tr').its(2).find('td').last().should('have.text', new_owner);
+ cy.get('@bucketDataTable').find('tr').its(11).find('td').last().as('versioningValueCell');
+
+ cy.get('@versioningValueCell').should('have.text', this.versioningStateEnabled);
+
+ // Disable versioning:
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+ this.expectBreadcrumbText('Edit');
+ cy.get('label[for=versioning]').click();
+ cy.get('input[id=versioning]').should('not.be.checked');
+ cy.contains('button', 'Edit Bucket').click();
+
+ // Check versioning suspended:
+ this.getExpandCollapseElement(name).click();
+
+ return cy.get('@versioningValueCell').should('have.text', this.versioningStateSuspended);
+ }
+
+ testInvalidCreate() {
+ this.navigateTo('create');
+ cy.get('#bid').as('nameInputField'); // Grabs name box field
+
+ // Gives an invalid name (too short), then waits for dashboard to determine validity
+ cy.get('@nameInputField').type('rq');
+
+ cy.contains('button', 'Create Bucket').click(); // To trigger a validation
+
+ // Waiting for website to decide if name is valid or not
+ // Check that name input field was marked invalid in the css
+ cy.get('@nameInputField')
+ .should('not.have.class', 'ng-pending')
+ .and('have.class', 'ng-invalid');
+
+ // Check that error message was printed under name input field
+ cy.get('#bid + .invalid-feedback').should('have.text', 'The value is not valid.');
+
+ // Test invalid owner input
+ // select some valid option. The owner drop down error message will not appear unless a valid user was selected at
+ // one point before the invalid placeholder user is selected.
+ this.selectOwner('dev');
+
+ // select the first option, which is invalid because it is a placeholder
+ this.selectOwner('-- Select a user --');
+
+ cy.get('@nameInputField').click();
+
+ // Check that owner drop down field was marked invalid in the css
+ cy.get('#owner').should('have.class', 'ng-invalid');
+
+ // Check that error message was printed under owner drop down field
+ cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+
+ // Check invalid placement target input
+ this.selectOwner('dev');
+ // The drop down error message will not appear unless a valid option is previsously selected.
+ this.selectPlacementTarget('default-placement');
+ this.selectPlacementTarget('-- Select a placement target --');
+ cy.get('@nameInputField').click(); // Trigger validation
+ cy.get('#placement-target').should('have.class', 'ng-invalid');
+ cy.get('#placement-target + .invalid-feedback').should('have.text', 'This field is required.');
+
+ // Clicks the Create Bucket button but the page doesn't move.
+ // Done by testing for the breadcrumb
+ cy.contains('button', 'Create Bucket').click(); // Clicks Create Bucket button
+ this.expectBreadcrumbText('Create');
+ // content in fields seems to subsist through tests if not cleared, so it is cleared
+ cy.get('@nameInputField').clear();
+ return cy.contains('button', 'Cancel').click();
+ }
+
+ testInvalidEdit(name: string) {
+ this.navigateTo();
+
+ this.getFirstTableCell(name).click(); // wait for table to load and click
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+
+ this.expectBreadcrumbText('Edit');
+
+ cy.get('input[id=versioning]').should('exist').and('not.be.checked');
+
+ // Chooses 'Select a user' rather than a valid owner on Edit Bucket page
+ // and checks if it's an invalid input
+
+ // select the first option, which is invalid because it is a placeholder
+ this.selectOwner('-- Select a user --');
+
+ cy.contains('button', 'Edit Bucket').click();
+
+ // Check that owner drop down field was marked invalid in the css
+ cy.get('#owner').should('have.class', 'ng-invalid');
+
+ // Check that error message was printed under owner drop down field
+ cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+
+ this.expectBreadcrumbText('Edit');
+ }
+}
--- /dev/null
+import { DaemonsPageHelper } from './daemons.po';
+
+describe('RGW daemons page', () => {
+ const daemons = new DaemonsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ daemons.navigateTo();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ it('should open and show breadcrumb', () => {
+ daemons.expectBreadcrumbText('Daemons');
+ });
+
+ it('should show two tabs', () => {
+ daemons.getTabsCount().should('eq', 2);
+ });
+
+ it('should show daemons list tab at first', () => {
+ daemons.getTabText(0).should('eq', 'Daemons List');
+ });
+
+ it('should show overall performance as a second tab', () => {
+ daemons.getTabText(1).should('eq', 'Overall Performance');
+ });
+ });
+
+ describe('details and performance counters table tests', () => {
+ it('should check that details/performance tables are visible when daemon is selected', () => {
+ daemons.checkTables();
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class DaemonsPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/rgw/daemon', id: 'cd-rgw-daemon-list' }
+ };
+
+ getTableCell(tableIndex: number) {
+ return cy
+ .get('.tab-container')
+ .its(1)
+ .find('cd-table')
+ .its(tableIndex)
+ .find('datatable-body-cell');
+ }
+
+ checkTables() {
+ // click on a daemon so details table appears
+ cy.get('.datatable-body-cell-label').first().click();
+
+ // check details table is visible
+ // check at least one field is present
+ this.getTableCell(0).should('visible').should('contain.text', 'ceph_version');
+ // check performance counters table is not currently visible
+ this.getTableCell(1).should('not.be.visible');
+
+ // click on performance counters tab and check table is loaded
+ cy.contains('.nav-link', 'Performance Counters').click();
+
+ // check at least one field is present
+ this.getTableCell(1).should('be.visible').should('contain.text', 'objecter.op_r');
+ // check details table is not currently visible
+ this.getTableCell(0).should('not.be.visible');
+
+ // click on performance details tab
+ cy.contains('.nav-link', 'Performance Details').click();
+
+ // checks the other tabs' content isn't visible
+ this.getTableCell(0).should('not.be.visible');
+ this.getTableCell(1).should('not.be.visible');
+ }
+}
--- /dev/null
+import { UsersPageHelper } from './users.po';
+
+describe('RGW users page', () => {
+ const users = new UsersPageHelper();
+ const user_name = 'e2e_000user_create_edit_delete';
+
+ beforeEach(() => {
+ cy.login();
+ users.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should open and show breadcrumb', () => {
+ users.expectBreadcrumbText('Users');
+ });
+ });
+
+ describe('create, edit & delete user tests', () => {
+ it('should create user', () => {
+ users.navigateTo('create');
+ users.create(user_name, 'Some Name', 'original@website.com', '1200');
+ users.getFirstTableCell(user_name).should('exist');
+ });
+
+ it('should edit users full name, email and max buckets', () => {
+ users.edit(user_name, 'Another Identity', 'changed@othersite.com', '1969');
+ });
+
+ it('should delete user', () => {
+ users.delete(user_name);
+ });
+ });
+
+ describe('Invalid input tests', () => {
+ it('should put invalid input into user creation form and check fields are marked invalid', () => {
+ users.invalidCreate();
+ });
+
+ it('should put invalid input into user edit form and check fields are marked invalid', () => {
+ users.invalidEdit();
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/rgw/user', id: 'cd-rgw-user-list' },
+ create: { url: '#/rgw/user/create', id: 'cd-rgw-user-form' }
+};
+
+export class UsersPageHelper extends PageHelper {
+ pages = pages;
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(username: string, fullname: string, email: string, maxbuckets: string) {
+ // Enter in username
+ cy.get('#uid').type(username);
+
+ // Enter in full name
+ cy.get('#display_name').click().type(fullname);
+
+ // Enter in email
+ cy.get('#email').click().type(email);
+
+ // Enter max buckets
+ this.selectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').click().clear().type(maxbuckets);
+
+ // Click the create button and wait for user to be made
+ cy.contains('button', 'Create User').click();
+ this.getFirstTableCell(username).should('exist');
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ edit(name: string, new_fullname: string, new_email: string, new_maxbuckets: string) {
+ this.getFirstTableCell(name).click(); // wait for table to load and click
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+
+ this.expectBreadcrumbText('Edit');
+
+ // Change the full name field
+ cy.get('#display_name').click().clear().type(new_fullname);
+
+ // Change the email field
+ cy.get('#email').click().clear().type(new_email);
+
+ // Change the max buckets field
+ this.selectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').click().clear().type(new_maxbuckets);
+
+ cy.contains('button', 'Edit User').click();
+
+ // Click the user and check its details table for updated content
+ this.getExpandCollapseElement(name).click();
+ cy.get('.active.tab-pane')
+ .should('contain.text', new_fullname)
+ .and('contain.text', new_email)
+ .and('contain.text', new_maxbuckets);
+ }
+
+ invalidCreate() {
+ const uname = '000invalid_create_user';
+ // creating this user in order to check that you can't give two users the same name
+ this.navigateTo('create');
+ this.create(uname, 'xxx', 'xxx@xxx', '1');
+
+ this.navigateTo('create');
+
+ // Username
+ cy.get('#uid')
+ // No username had been entered. Field should be invalid
+ .should('have.class', 'ng-invalid')
+ // Try to give user already taken name. Should make field invalid.
+ .type(uname)
+ .blur()
+ .should('have.class', 'ng-invalid');
+ cy.contains('#uid + .invalid-feedback', 'The chosen user ID is already in use.');
+
+ // check that username field is marked invalid if username has been cleared off
+ cy.get('#uid').clear().blur().should('have.class', 'ng-invalid');
+ cy.contains('#uid + .invalid-feedback', 'This field is required.');
+
+ // Full name
+ cy.get('#display_name')
+ // No display name has been given so field should be invalid
+ .should('have.class', 'ng-invalid')
+ // display name field should also be marked invalid if given input then emptied
+ .type('a')
+ .clear()
+ .blur()
+ .should('have.class', 'ng-invalid');
+ cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+
+ // put invalid email to make field invalid
+ cy.get('#email').type('a').blur().should('have.class', 'ng-invalid');
+ cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+
+ // put negative max buckets to make field invalid
+ this.expectSelectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
+ cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+
+ this.navigateTo();
+ this.delete(uname);
+ }
+
+ invalidEdit() {
+ const uname = '000invalid_edit_user';
+ // creating this user to edit for the test
+ this.navigateTo('create');
+ this.create(uname, 'xxx', 'xxx@xxx', '1');
+
+ this.navigateTo();
+
+ // wait for table to load and click on the bucket you want to edit in the table
+ this.getFirstTableCell(uname).click();
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+
+ this.expectBreadcrumbText('Edit');
+
+ // put invalid email to make field invalid
+ cy.get('#email')
+ .clear()
+ .type('a')
+ .blur()
+ .should('not.have.class', 'ng-pending')
+ .should('have.class', 'ng-invalid');
+ cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+
+ // empty the display name field making it invalid
+ cy.get('#display_name').clear().blur().should('have.class', 'ng-invalid');
+ cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+
+ // put negative max buckets to make field invalid
+ this.expectSelectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
+ cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+
+ this.navigateTo();
+ this.delete(uname);
+ }
+}
--- /dev/null
+import { IscsiPageHelper } from '../block/iscsi.po';
+import { HostsPageHelper } from '../cluster/hosts.po';
+import { MonitorsPageHelper } from '../cluster/monitors.po';
+import { OSDsPageHelper } from '../cluster/osds.po';
+import { PageHelper } from '../page-helper.po';
+import { PoolPageHelper } from '../pools/pools.po';
+import { DaemonsPageHelper } from '../rgw/daemons.po';
+import { DashboardPageHelper } from './dashboard.po';
+
+describe('Dashboard Main Page', () => {
+ const dashboard = new DashboardPageHelper();
+ const daemons = new DaemonsPageHelper();
+ const hosts = new HostsPageHelper();
+ const osds = new OSDsPageHelper();
+ const pools = new PoolPageHelper();
+ const monitors = new MonitorsPageHelper();
+ const iscsi = new IscsiPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ dashboard.navigateTo();
+ });
+
+ describe('Check that all hyperlinks on info cards lead to the correct page and fields exist', () => {
+ it('should ensure that all linked info cards lead to correct page', () => {
+ const expectationMap = {
+ Monitors: 'Monitors',
+ OSDs: 'OSDs',
+ Hosts: 'Hosts',
+ 'Object Gateways': 'Daemons',
+ 'iSCSI Gateways': 'Overview',
+ Pools: 'Pools'
+ };
+
+ for (const [linkText, breadcrumbText] of Object.entries(expectationMap)) {
+ cy.location('hash').should('eq', '#/dashboard');
+ dashboard.clickInfoCardLink(linkText);
+ dashboard.expectBreadcrumbText(breadcrumbText);
+ dashboard.navigateBack();
+ }
+ });
+
+ it('should verify that info cards exist on dashboard in proper order', () => {
+ // Ensures that info cards are all displayed on the dashboard tab while being in the proper
+ // order, checks for card title and position via indexing into a list of all info cards.
+ const order = [
+ 'Cluster Status',
+ 'Monitors',
+ 'OSDs',
+ 'Manager Daemons',
+ 'Hosts',
+ 'Object Gateways',
+ 'Metadata Servers',
+ 'iSCSI Gateways',
+ 'Client IOPS',
+ 'Client Throughput',
+ 'Client Read/Write',
+ 'Recovery Throughput',
+ 'Scrub',
+ 'Pools',
+ 'Raw Capacity',
+ 'Objects',
+ 'PGs per OSD',
+ 'PG Status'
+ ];
+
+ for (let i = 0; i < order.length; i++) {
+ dashboard.infoCard(i).should('contain.text', order[i]);
+ }
+ });
+
+ it('should verify that info card group titles are present and in the right order', () => {
+ cy.location('hash').should('eq', '#/dashboard');
+ dashboard.infoGroupTitle(0).should('eq', 'Status');
+ dashboard.infoGroupTitle(1).should('eq', 'Performance');
+ dashboard.infoGroupTitle(2).should('eq', 'Capacity');
+ });
+ });
+
+ it('Should check that dashboard cards have correct information', () => {
+ interface TestSpec {
+ cardName: string;
+ regexMatcher?: RegExp;
+ pageObject: PageHelper;
+ }
+ const testSpecs: TestSpec[] = [
+ { cardName: 'Object Gateways', regexMatcher: /(\d+)\s+total/, pageObject: daemons },
+ { cardName: 'Monitors', regexMatcher: /(\d+)\s+\(quorum/, pageObject: monitors },
+ { cardName: 'Hosts', regexMatcher: /(\d+)\s+total/, pageObject: hosts },
+ { cardName: 'OSDs', regexMatcher: /(\d+)\s+total/, pageObject: osds },
+ { cardName: 'Pools', pageObject: pools },
+ { cardName: 'iSCSI Gateways', regexMatcher: /(\d+)\s+total/, pageObject: iscsi }
+ ];
+ for (let i = 0; i < testSpecs.length; i++) {
+ const spec = testSpecs[i];
+ dashboard.navigateTo();
+
+ dashboard.infoCardBodyText(spec.cardName).then((infoCardBodyText: string) => {
+ let dashCount = 0;
+
+ if (spec.regexMatcher) {
+ const match = infoCardBodyText.match(new RegExp(spec.regexMatcher));
+ expect(match).to.length.gt(
+ 1,
+ `Regex ${spec.regexMatcher} did not find a match for card with name ` +
+ `${spec.cardName}`
+ );
+ dashCount = Number(match[1]);
+ } else {
+ dashCount = Number(infoCardBodyText);
+ }
+
+ spec.pageObject.navigateTo();
+ spec.pageObject.getTableTotalCount().then((tableCount) => {
+ expect(tableCount).to.eq(
+ dashCount,
+ `Text of card "${spec.cardName}" and regex "${spec.regexMatcher}" resulted in ${dashCount} ` +
+ `but did not match table count ${tableCount}`
+ );
+ });
+ });
+ }
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class DashboardPageHelper extends PageHelper {
+ pages = { index: { url: '#/dashboard', id: 'cd-dashboard' } };
+
+ infoGroupTitle(index: number) {
+ return cy.get('.info-group-title').its(index).text();
+ }
+
+ clickInfoCardLink(cardName: string) {
+ cy.get(`cd-info-card[cardtitle="${cardName}"]`).contains('a', cardName).click();
+ }
+
+ infoCard(indexOrTitle: number | string) {
+ cy.get('cd-info-card').as('infoCards');
+
+ if (typeof indexOrTitle === 'number') {
+ return cy.get('@infoCards').its(indexOrTitle);
+ } else {
+ return cy.contains('cd-info-card a', indexOrTitle).parent().parent().parent().parent();
+ }
+ }
+
+ infoCardBodyText(infoCard: string) {
+ return this.infoCard(infoCard).find('.card-text').text();
+ }
+}
--- /dev/null
+import { PoolPageHelper } from '../pools/pools.po';
+import { NotificationSidebarPageHelper } from './notification.po';
+
+describe('Notification page', () => {
+ const notification = new NotificationSidebarPageHelper();
+ const pools = new PoolPageHelper();
+ const poolName = 'e2e_notification_pool';
+
+ before(() => {
+ cy.login();
+ pools.navigateTo('create');
+ pools.create(poolName, 8);
+ pools.edit_pool_pg(poolName, 4, false);
+ });
+
+ after(() => {
+ cy.login();
+ pools.navigateTo();
+ pools.delete(poolName);
+ });
+
+ beforeEach(() => {
+ cy.login();
+ pools.navigateTo();
+ });
+
+ it('should open notification sidebar', () => {
+ notification.getSidebar().should('not.be.visible');
+ notification.open();
+ notification.getSidebar().should('be.visible');
+ });
+
+ it('should display a running task', () => {
+ notification.getToast().should('not.exist');
+
+ // Check that running task is shown.
+ notification.open();
+ notification.getTasks().contains(poolName).should('exist');
+
+ // Delete pool after task is complete (otherwise we get an error).
+ notification.getTasks().contains(poolName, { timeout: 300000 }).should('not.exist');
+ });
+
+ it('should have notifications', () => {
+ notification.open();
+ notification.getNotifications().should('have.length.gt', 0);
+ });
+
+ it('should clear notifications', () => {
+ notification.getToast().should('not.exist');
+ notification.open();
+ notification.getNotifications().should('have.length.gt', 0);
+ notification.getClearNotficationsBtn().should('be.visible');
+ notification.clearNotifications();
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class NotificationSidebarPageHelper extends PageHelper {
+ getNotificatinoIcon() {
+ return cy.get('cd-notifications a');
+ }
+
+ getSidebar() {
+ return cy.get('cd-notifications-sidebar');
+ }
+
+ getTasks() {
+ return this.getSidebar().find('.card.tc_task');
+ }
+
+ getNotifications() {
+ return this.getSidebar().find('.card.tc_notification');
+ }
+
+ getClearNotficationsBtn() {
+ return this.getSidebar().find('button.btn-block');
+ }
+
+ getCloseBtn() {
+ return this.getSidebar().find('button.close');
+ }
+
+ open() {
+ this.getNotificatinoIcon().click();
+ this.getSidebar().should('be.visible');
+ }
+
+ clearNotifications() {
+ // It can happen that although notifications are cleared, by the time we check the notifications
+ // amount, another notification can appear, so we check it more than once (if needed).
+ this.getClearNotficationsBtn().click();
+ this.getNotifications()
+ .should('have.length.gte', 0)
+ .then(($elems) => {
+ if ($elems.length > 0) {
+ this.clearNotifications();
+ }
+ });
+ }
+}
--- /dev/null
+import { RoleMgmtPageHelper } from './role-mgmt.po';
+
+describe('Role Management page', () => {
+ const roleMgmt = new RoleMgmtPageHelper();
+ const role_name = 'e2e_role_mgmt_role';
+
+ beforeEach(() => {
+ cy.login();
+ roleMgmt.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should check breadcrumb on roles tab on user management page', () => {
+ roleMgmt.expectBreadcrumbText('Roles');
+ });
+
+ it('should check breadcrumb on role creation page', () => {
+ roleMgmt.navigateTo('create');
+ roleMgmt.expectBreadcrumbText('Create');
+ });
+ });
+
+ describe('role create, edit & delete test', () => {
+ it('should create a role', () => {
+ roleMgmt.create(role_name, 'An interesting description');
+ });
+
+ it('should edit a role', () => {
+ roleMgmt.edit(role_name, 'A far more interesting description');
+ });
+
+ it('should delete a role', () => {
+ roleMgmt.delete(role_name);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class RoleMgmtPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/user-management/roles', id: 'cd-role-list' },
+ create: { url: '#/user-management/roles/create', id: 'cd-role-form' }
+ };
+
+ create(name: string, description: string) {
+ this.navigateTo('create');
+
+ // fill in fields
+ cy.get('#name').type(name);
+ cy.get('#description').type(description);
+
+ // Click the create button and wait for role to be made
+ cy.contains('button', 'Create Role').click();
+
+ this.getFirstTableCell(name).should('exist');
+ }
+
+ edit(name: string, description: string) {
+ this.getFirstTableCell(name).click(); // select role from table
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+
+ // fill in fields with new values
+ cy.get('#description').clear().type(description);
+
+ // Click the edit button and check new values are present in table
+ cy.contains('button', 'Edit Role').click();
+
+ this.getFirstTableCell(name).should('exist');
+ this.getFirstTableCell(description).should('exist');
+ }
+}
--- /dev/null
+import { UserMgmtPageHelper } from './user-mgmt.po';
+
+describe('User Management page', () => {
+ const userMgmt = new UserMgmtPageHelper();
+ const user_name = 'e2e_user_mgmt_user';
+
+ beforeEach(() => {
+ cy.login();
+ userMgmt.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should check breadcrumb on users tab of user management page', () => {
+ userMgmt.expectBreadcrumbText('Users');
+ });
+
+ it('should check breadcrumb on user creation page', () => {
+ userMgmt.navigateTo('create');
+ userMgmt.expectBreadcrumbText('Create');
+ });
+ });
+
+ describe('user create, edit & delete test', () => {
+ it('should create a user', () => {
+ userMgmt.create(user_name, 'cool_password', 'Jeff', 'realemail@realwebsite.com');
+ });
+
+ it('should edit a user', () => {
+ userMgmt.edit(user_name, 'cool_password_number_2', 'Geoff', 'w@m');
+ });
+
+ it('should delete a user', () => {
+ userMgmt.delete(user_name);
+ });
+ });
+});
--- /dev/null
+import { PageHelper } from '../page-helper.po';
+
+export class UserMgmtPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/user-management/users', id: 'cd-user-list' },
+ create: { url: '#/user-management/users/create', id: 'cd-user-form' }
+ };
+
+ create(username: string, password: string, name: string, email: string) {
+ this.navigateTo('create');
+
+ // fill in fields
+ cy.get('#username').type(username);
+ cy.get('#password').type(password);
+ cy.get('#confirmpassword').type(password);
+ cy.get('#name').type(name);
+ cy.get('#email').type(email);
+
+ // Click the create button and wait for user to be made
+ cy.contains('button', 'Create User').click();
+ this.getFirstTableCell(username).should('exist');
+ }
+
+ edit(username: string, password: string, name: string, email: string) {
+ this.getFirstTableCell(username).click(); // select user from table
+ cy.contains('button', 'Edit').click(); // click button to move to edit page
+
+ // fill in fields with new values
+ cy.get('#password').clear().type(password);
+ cy.get('#confirmpassword').clear().type(password);
+ cy.get('#name').clear().type(name);
+ cy.get('#email').clear().type(email);
+
+ // Click the edit button and check new values are present in table
+ const editButton = cy.contains('button', 'Edit User');
+ editButton.click();
+ this.getFirstTableCell(email).should('exist');
+ this.getFirstTableCell(name).should('exist');
+ }
+}
--- /dev/null
+declare global {
+ namespace Cypress {
+ interface Chainable<Subject> {
+ login(): void;
+ text(): Chainable<string>;
+ }
+ }
+}
+
+import { Permissions } from '../../src/app/shared/models/permissions';
+
+let auth: any;
+
+const fillAuth = () => {
+ window.localStorage.setItem('dashboard_username', auth.username);
+ window.localStorage.setItem('access_token', auth.token);
+ window.localStorage.setItem('dashboard_permissions', auth.permissions);
+ window.localStorage.setItem('user_pwd_expiration_date', auth.pwdExpirationDate);
+ window.localStorage.setItem('user_pwd_update_required', auth.pwdUpdateRequired);
+ window.localStorage.setItem('sso', auth.sso);
+};
+
+Cypress.Commands.add('login', () => {
+ const username = Cypress.env('LOGIN_USER') || 'admin';
+ const password = Cypress.env('LOGIN_PWD') || 'admin';
+
+ if (auth === undefined) {
+ cy.request({
+ method: 'POST',
+ url: 'api/auth',
+ body: { username: username, password: password }
+ }).then((resp) => {
+ auth = resp.body;
+ auth.permissions = JSON.stringify(new Permissions(auth.permissions));
+ auth.pwdExpirationDate = String(auth.pwdExpirationDate);
+ auth.pwdUpdateRequired = String(auth.pwdUpdateRequired);
+ auth.sso = String(auth.sso);
+ fillAuth();
+ });
+ } else {
+ fillAuth();
+ }
+});
+
+Cypress.Commands.add('text', { prevSubject: true }, (subject) => {
+ return subject.text();
+});
--- /dev/null
+import './commands';
+
+afterEach(() => {
+ cy.visit('#/403');
+});
--- /dev/null
+{
+ "extends": "../tsconfig.json",
+ "exclude": [],
+ "include": [
+ "**/*.ts"
+ ],
+ "compilerOptions": {
+ "types": [
+ "cypress"
+ ],
+ "target": "es6"
+ }
+}
+++ /dev/null
-import { PoolPageHelper } from '../pools/pools.po';
-import { ImagesPageHelper } from './images.po';
-
-describe('Images page', () => {
- let pools: PoolPageHelper;
- let images: ImagesPageHelper;
-
- beforeAll(() => {
- images = new ImagesPageHelper();
- pools = new PoolPageHelper();
- });
-
- afterEach(async () => {
- await ImagesPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- beforeAll(async () => {
- await images.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await images.waitTextToBePresent(images.getBreadcrumb(), 'Images');
- });
-
- it('should show four tabs', async () => {
- await expect(images.getTabsCount()).toEqual(4);
- });
-
- it('should show text for all tabs', async () => {
- await expect(images.getTabText(0)).toEqual('Images');
- await expect(images.getTabText(1)).toEqual('Namespaces');
- await expect(images.getTabText(2)).toEqual('Trash');
- await expect(images.getTabText(3)).toEqual('Overall Performance');
- });
- });
-
- describe('create, edit & delete image test', () => {
- const poolName = 'e2e_images_pool';
- const imageName = 'e2e_images#image';
- const newImageName = 'e2e_images#image_new';
-
- beforeAll(async () => {
- await pools.navigateTo('create'); // Need pool for image testing
- await pools.create(poolName, 8, 'rbd');
- await pools.navigateTo();
- await pools.exist(poolName, true);
- await images.navigateTo();
- });
-
- it('should create image', async () => {
- await images.createImage(imageName, poolName, '1');
- await expect(images.getFirstTableCellWithText(imageName).isPresent()).toBe(true);
- });
-
- it('should edit image', async () => {
- await images.editImage(imageName, poolName, newImageName, '2');
- await expect(images.getFirstTableCellWithText(newImageName).isPresent()).toBe(true);
- });
-
- it('should delete image', async () => {
- await images.navigateTo();
- await images.delete(newImageName);
- });
-
- afterAll(async () => {
- await pools.navigateTo();
- await pools.delete(poolName);
- });
- });
-
- describe('move to trash, restore and purge image tests', () => {
- const poolName = 'trash_pool';
- const imageName = 'trash#image';
- const newImageName = 'newtrash#image';
-
- beforeAll(async () => {
- await pools.navigateTo('create'); // Need pool for image testing
- await pools.create(poolName, 8, 'rbd');
- await pools.navigateTo();
- await pools.exist(poolName, true);
-
- await images.navigateTo(); // Need image for trash testing
- await images.createImage(imageName, poolName, '1');
- await expect(images.getFirstTableCellWithText(imageName).isPresent()).toBe(true);
- });
-
- it('should move the image to the trash', async () => {
- await images.moveToTrash(imageName);
- await expect(images.getFirstTableCellWithText(imageName).isPresent()).toBe(true);
- });
-
- it('should restore image to images table', async () => {
- await images.restoreImage(imageName, newImageName);
- await expect(images.getFirstTableCellWithText(newImageName).isPresent()).toBe(true);
- });
-
- it('should purge trash in images trash tab', async () => {
- await images.navigateTo();
- // Have had issues with image not restoring fast enough, thus these tests/waits are here
- await images.waitPresence(
- images.getFirstTableCellWithText(newImageName),
- 'Timed out waiting for image to restore'
- );
- await images.moveToTrash(newImageName);
- await images.purgeTrash(newImageName, poolName);
- });
-
- afterAll(async () => {
- await pools.navigateTo();
- await pools.delete(poolName); // Deletes images test pool
- await pools.navigateTo();
- await pools.exist(poolName, false);
- });
- });
-});
+++ /dev/null
-import { $, $$, browser, by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class ImagesPageHelper extends PageHelper {
- pages = {
- index: '/#/block/rbd',
- create: '/#/block/rbd/create'
- };
-
- // Creates a block image and fills in the name, pool, and size fields. Then checks
- // if the image is present in the Images table.
- async createImage(name: string, pool: string, size: string) {
- await this.navigateTo('create');
-
- // Need the string '[value="<pool>"]' to find the pool in the dropdown menu
- const getPoolName = `[value="${pool}"]`;
-
- await element(by.id('name')).sendKeys(name); // Enter in image name
-
- // Select image pool
- await this.selectOption('pool', pool);
- await $(getPoolName).click();
- await expect(element(by.id('pool')).getAttribute('class')).toContain('ng-valid'); // check if selected
-
- // Enter in the size of the image
- await element(by.id('size')).click();
- await element(by.id('size')).sendKeys(size);
-
- // Click the create button and wait for image to be made
- await element(by.cssContainingText('button', 'Create RBD')).click();
- return this.waitPresence(this.getFirstTableCellWithText(name));
- }
-
- async editImage(name: string, pool: string, newName: string, newSize: string) {
- const base_url = '/#/block/rbd/edit/';
- const editURL = base_url
- .concat(encodeURIComponent(pool))
- .concat('%2F')
- .concat(encodeURIComponent(name));
- await browser.get(editURL);
-
- await element(by.id('name')).click(); // click name box and send new name
- await element(by.id('name')).clear();
- await element(by.id('name')).sendKeys(newName);
- await element(by.id('size')).click();
- await element(by.id('size')).clear();
- await element(by.id('size')).sendKeys(newSize); // click the size box and send new size
-
- await element(by.cssContainingText('button', 'Edit RBD')).click();
- await this.navigateTo();
- await this.waitClickableAndClick(this.getExpandCollapseElement(newName));
- await expect(
- element.all(by.css('.table.table-striped.table-bordered')).first().getText()
- ).toMatch(newSize);
- }
-
- // Selects RBD image and moves it to the trash, checks that it is present in the
- // trash table
- async moveToTrash(name: string) {
- await this.navigateTo();
- // wait for image to be created
- await this.waitTextNotPresent($$('.datatable-body').first(), '(Creating...)');
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
- // click on the drop down and selects the move to trash option
- await $$('.table-actions button.dropdown-toggle').first().click();
- await $('li.move-to-trash').click();
- await this.waitVisibility(element(by.cssContainingText('button', 'Move Image')));
- await element(by.cssContainingText('button', 'Move Image')).click();
- await this.navigateTo();
- // Clicks trash tab
- await this.waitClickableAndClick(element(by.cssContainingText('.nav-link', 'Trash')));
- await this.waitPresence(this.getFirstTableCellWithText(name));
- }
-
- // Checks trash tab table for image and then restores it to the RBD Images table
- // (could change name if new name is given)
- async restoreImage(name: string, newName?: string) {
- await this.navigateTo();
- // clicks on trash tab
- await element(by.cssContainingText('.nav-link', 'Trash')).click();
- // wait for table to load
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
- await element(by.cssContainingText('button', 'Restore')).click();
- // wait for pop-up to be visible (checks for title of pop-up)
- await this.waitVisibility(element(by.id('name')));
- // If a new name for the image is passed, it changes the name of the image
- if (newName !== undefined) {
- await element(by.id('name')).click(); // click name box and send new name
- await element(by.id('name')).clear();
- await element(by.id('name')).sendKeys(newName);
- }
- await element(by.cssContainingText('button', 'Restore Image')).click();
- await this.navigateTo();
- // clicks images tab
- await element(by.cssContainingText('.nav-link', 'Images')).click();
- await this.navigateTo();
- await this.waitPresence(this.getFirstTableCellWithText(newName));
- }
-
- // Enters trash tab and purges trash, thus emptying the trash table. Checks if
- // Image is still in the table.
- async purgeTrash(name: string, pool?: string) {
- await this.navigateTo();
- // clicks trash tab
- await element(by.cssContainingText('.nav-link', 'Trash')).click();
- await element(by.cssContainingText('button', 'Purge Trash')).click();
- // Check for visibility of modal container
- await this.waitVisibility(element(by.id('poolName')));
- // If purgeing a specific pool, selects that pool if given
- if (pool !== undefined) {
- const getPoolName = `[value="${pool}"]`;
- await element(by.id('poolName')).click();
- await element(by.cssContainingText('select[name=poolName] option', pool)).click();
- await $(getPoolName).click();
- await expect(element(by.id('poolName')).getAttribute('class')).toContain('ng-valid'); // check if pool is selected
- }
- await this.waitClickableAndClick(element(by.id('purgeFormButton')));
- // Wait for image to delete and check it is not present
- await this.waitStaleness(
- this.getFirstTableCellWithText(name),
- 'Timed out waiting for image to be purged'
- );
- await expect(this.getFirstTableCellWithText(name).isPresent()).toBe(false);
- }
-}
+++ /dev/null
-import { IscsiPageHelper } from './iscsi.po';
-
-describe('Iscsi Page', () => {
- let iscsi: IscsiPageHelper;
-
- beforeAll(() => {
- iscsi = new IscsiPageHelper();
- });
-
- afterEach(async () => {
- await IscsiPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await iscsi.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await iscsi.waitTextToBePresent(iscsi.getBreadcrumb(), 'Overview');
- });
- });
-
- describe('fields check', () => {
- beforeAll(async () => {
- await iscsi.navigateTo();
- });
-
- it('should check that tables are displayed and legends are correct', async () => {
- // Check tables are displayed
- const dataTables = iscsi.getDataTables();
- await expect(dataTables.get(0).isDisplayed());
- await expect(dataTables.get(1).isDisplayed());
-
- // Check that legends are correct
- const legends = iscsi.getLegends();
- await expect(legends.get(0).getText()).toMatch('Gateways');
- await expect(legends.get(1).getText()).toMatch('Images');
- });
- });
-});
+++ /dev/null
-import { PageHelper } from '../page-helper.po';
-
-export class IscsiPageHelper extends PageHelper {
- pages = { index: '/#/block/iscsi/overview' };
-}
+++ /dev/null
-import { PoolPageHelper } from '../pools/pools.po';
-import { MirroringPageHelper } from './mirroring.po';
-
-describe('Mirroring page', () => {
- let pools: PoolPageHelper;
- let mirroring: MirroringPageHelper;
-
- beforeAll(() => {
- mirroring = new MirroringPageHelper();
- pools = new PoolPageHelper();
- });
-
- afterEach(async () => {
- await MirroringPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- beforeAll(async () => {
- await mirroring.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await mirroring.waitTextToBePresent(mirroring.getBreadcrumb(), 'Mirroring');
- });
-
- it('should show three tabs', async () => {
- await expect(mirroring.getTabsCount()).toEqual(3);
- });
-
- it('should show text for all tabs', async () => {
- await expect(mirroring.getTabText(0)).toEqual('Issues');
- await expect(mirroring.getTabText(1)).toEqual('Syncing');
- await expect(mirroring.getTabText(2)).toEqual('Ready');
- });
- });
-
- describe('checks that edit mode functionality shows in the pools table', () => {
- const poolName = 'mirroring_test';
-
- beforeAll(async () => {
- await pools.navigateTo('create'); // Need pool for mirroring testing
- await pools.create(poolName, 8, 'rbd');
- await pools.navigateTo();
- await pools.exist(poolName, true);
- });
-
- it('tests editing mode for pools', async () => {
- await mirroring.navigateTo();
-
- await mirroring.editMirror(poolName, 'Pool');
- await expect(mirroring.getFirstTableCellWithText('pool').isPresent()).toBe(true);
- await mirroring.editMirror(poolName, 'Image');
- await expect(mirroring.getFirstTableCellWithText('image').isPresent()).toBe(true);
- await mirroring.editMirror(poolName, 'Disabled');
- await expect(mirroring.getFirstTableCellWithText('disabled').isPresent()).toBe(true);
- });
-
- afterAll(async () => {
- await pools.navigateTo();
- await pools.delete(poolName);
- });
- });
-});
+++ /dev/null
-import { $, by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-const pages = { index: '/#/block/mirroring' };
-
-export class MirroringPageHelper extends PageHelper {
- pages = pages;
-
- /**
- * Goes to the mirroring page and edits a pool in the Pool table. Clicks on the
- * pool and chooses an option (either pool, image, or disabled)
- */
- @PageHelper.restrictTo(pages.index)
- async editMirror(name: string, option: string) {
- // Clicks the pool in the table
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
-
- // Clicks the Edit Mode button
- const editModeButton = element(by.cssContainingText('button', 'Edit Mode'));
- await this.waitClickableAndClick(editModeButton);
- // Clicks the drop down in the edit pop-up, then clicks the Update button
- await this.waitVisibility($('.modal-content'));
- await this.selectOption('mirrorMode', option);
-
- // Clicks update button and checks if the mode has been changed
- await element(by.cssContainingText('button', 'Update')).click();
- await this.waitStaleness(
- element(by.cssContainingText('.modal-dialog', 'Edit pool mirror mode'))
- );
- const val = option.toLowerCase(); // used since entries in table are lower case
- await this.waitVisibility(this.getFirstTableCellWithText(val));
- }
-}
+++ /dev/null
-import { $ } from 'protractor';
-import { ConfigurationPageHelper } from './configuration.po';
-
-describe('Configuration page', () => {
- let configuration: ConfigurationPageHelper;
-
- beforeAll(() => {
- configuration = new ConfigurationPageHelper();
- });
-
- afterEach(async () => {
- await ConfigurationPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await configuration.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await configuration.waitTextToBePresent(configuration.getBreadcrumb(), 'Configuration');
- });
- });
-
- describe('fields check', () => {
- beforeAll(async () => {
- await configuration.navigateTo();
- await configuration.waitClickableAndClick(configuration.getFirstExpandCollapseElement());
- });
-
- it('should verify that selected footer increases when an entry is clicked', async () => {
- const selectedCount = await configuration.getTableSelectedCount();
- await expect(selectedCount).toBe(1);
- });
-
- it('should check that details table opens and tab is correct', async () => {
- await expect($('.table.table-striped.table-bordered').isDisplayed());
- await expect(configuration.getTabsCount()).toEqual(1);
- await expect(configuration.getTabText(0)).toEqual('Details');
- });
- });
-
- describe('edit configuration test', () => {
- const configName = 'client_cache_size';
-
- beforeAll(async () => {
- await configuration.navigateTo();
- });
-
- beforeEach(async () => {
- await configuration.clearTableSearchInput();
- });
-
- afterAll(async () => {
- await configuration.configClear(configName);
- });
-
- it('should click and edit a configuration and results should appear in the table', async () => {
- await configuration.edit(
- configName,
- ['global', '1'],
- ['mon', '2'],
- ['mgr', '3'],
- ['osd', '4'],
- ['mds', '5'],
- ['client', '6']
- );
- });
-
- it('should show only modified configurations', async () => {
- await configuration.filterTable('Modified', 'yes');
- expect(await configuration.getTableFoundCount()).toBe(1);
- });
-
- it('should hide all modified configurations', async () => {
- await configuration.filterTable('Modified', 'no');
- expect(await configuration.getTableFoundCount()).toBeGreaterThan(1);
- });
- });
-});
+++ /dev/null
-import { $, by, element, protractor } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class ConfigurationPageHelper extends PageHelper {
- pages = {
- index: '/#/configuration'
- };
-
- async configClear(name: string) {
- // Clears out all the values in a config to reset before and after testing
- // Does not work for configs with checkbox only, possible future PR
-
- await this.navigateTo();
- const valList = ['global', 'mon', 'mgr', 'osd', 'mds', 'client']; // Editable values
-
- // Enter config setting name into filter box
- await $('input.form-control.ng-valid').clear();
- await $('input.form-control.ng-valid').sendKeys(name);
-
- // Selects config that we want to clear
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // waits for config to be clickable and click
- await element(by.cssContainingText('button', 'Edit')).click(); // clicks button to edit
-
- for (const i of valList) {
- // Sends two backspaces to all values, clear() did not work in this instance, could be optimized more
- await element(by.id(i)).sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a'));
- await element(by.id(i)).sendKeys(protractor.Key.BACK_SPACE);
- }
- // Clicks save button and checks that values are not present for the selected config
- await element(by.cssContainingText('button', 'Save')).click();
-
- // Enter config setting name into filter box
- await $('input.form-control.ng-valid').clear();
- await $('input.form-control.ng-valid').sendKeys(name);
-
- // Expand row
- await this.waitClickableAndClick(this.getExpandCollapseElement(name));
- // Clicks desired config
- await this.waitVisibility(
- $('.table.table-striped.table-bordered'), // Checks for visibility of details tab
- 'config details did not appear'
- );
- for (const i of valList) {
- // Waits until values are not present in the details table
- await this.waitTextNotPresent($('.table.table-striped.table-bordered'), i + ':');
- }
- }
-
- async edit(name: string, ...values: [string, string][]) {
- // Clicks the designated config, then inputs the values passed into the edit function.
- // Then checks if the edit is reflected in the config table. Takes in name of config and
- // a list of tuples of values the user wants edited, each tuple having the desired value along
- // with the number tehey want for that value. Ex: [global, '2'] is the global value with an input of 2
- await this.navigateTo();
-
- // Enter config setting name into filter box
- await $('input.form-control.ng-valid').clear();
- await $('input.form-control.ng-valid').sendKeys(name);
-
- // Selects config that we want to edit
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // waits for config to be clickable and click
- await element(by.cssContainingText('button', 'Edit')).click(); // clicks button to edit
-
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
-
- for (let i = 0, valtuple; (valtuple = values[i]); i++) {
- // Finds desired value based off given list
- await element(by.id(valtuple[0])).sendKeys(valtuple[1]); // of values and inserts the given number for the value
- }
-
- // Clicks save button then waits until the desired config is visible, clicks it, then checks
- // that each desired value appears with the desired number
- await element(by.cssContainingText('button', 'Save')).click();
- await this.navigateTo();
-
- // Enter config setting name into filter box
- await $('input.form-control.ng-valid').clear();
- await $('input.form-control.ng-valid').sendKeys(name);
-
- await this.waitVisibility(this.getFirstTableCellWithText(name));
- // Checks for visibility of config in table
- await this.getExpandCollapseElement(name).click();
- // Clicks config
- for (let i = 0, valtuple; (valtuple = values[i]); i++) {
- // iterates through list of values and
- await this.waitTextToBePresent(
- // checks if the value appears in details with the correct number attatched
- $('.table.table-striped.table-bordered'),
- valtuple[0] + ': ' + valtuple[1]
- );
- }
- }
-}
+++ /dev/null
-import { $ } from 'protractor';
-import { CrushMapPageHelper } from './crush-map.po';
-
-describe('CRUSH map page', () => {
- let crushmap: CrushMapPageHelper;
-
- beforeAll(() => {
- crushmap = new CrushMapPageHelper();
- });
-
- afterEach(async () => {
- await CrushMapPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await crushmap.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await crushmap.waitTextToBePresent(crushmap.getBreadcrumb(), 'CRUSH map');
- });
- });
- describe('fields check', () => {
- beforeAll(async () => {
- await crushmap.navigateTo();
- });
-
- it('should check that title & table appears', async () => {
- // Check that title (CRUSH map viewer) appears
- await expect(crushmap.getPageTitle()).toMatch('CRUSH map viewer');
-
- // Check that title appears once OSD is clicked
- await crushmap.getCrushNode(1).click();
-
- const label = await $('legend').getText(); // Get table label
- await expect(crushmap.getCrushNode(1).getText()).toEqual(label);
-
- // Check that table appears once OSD is clicked
- await crushmap.waitVisibility($('.datatable-body'));
- await expect($('.datatable-body').isDisplayed()).toBe(true);
- });
- });
-});
+++ /dev/null
-import { $, $$ } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class CrushMapPageHelper extends PageHelper {
- pages = { index: '/#/crush-map' };
-
- getPageTitle() {
- return $('cd-crushmap .card-header').getText();
- }
-
- getCrushNode(idx: number) {
- return $$('.node-name.type-osd').get(idx);
- }
-}
+++ /dev/null
-import { HostsPageHelper } from './hosts.po';
-
-describe('Hosts page', () => {
- let hosts: HostsPageHelper;
-
- beforeAll(() => {
- hosts = new HostsPageHelper();
- });
-
- afterEach(async () => {
- await HostsPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- beforeAll(async () => {
- await hosts.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await hosts.waitTextToBePresent(hosts.getBreadcrumb(), 'Hosts');
- });
-
- it('should show two tabs', async () => {
- await expect(hosts.getTabsCount()).toEqual(2);
- });
-
- it('should show hosts list tab at first', async () => {
- await expect(hosts.getTabText(0)).toEqual('Hosts List');
- });
-
- it('should show overall performance as a second tab', async () => {
- await expect(hosts.getTabText(1)).toEqual('Overall Performance');
- });
- });
-
- describe('services link test', () => {
- it('should check at least one host is present', async () => {
- await hosts.check_for_host();
- });
-
- it('should check services link(s) work for first host', async () => {
- await hosts.check_services_links();
- });
- });
-});
+++ /dev/null
-import { by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class HostsPageHelper extends PageHelper {
- pages = { index: '/#/hosts' };
-
- async check_for_host() {
- await this.navigateTo();
-
- await expect(this.getTableTotalCount()).not.toBe(0);
- }
-
- // function that checks all services links work for first
- // host in table
- async check_services_links() {
- await this.navigateTo();
- let links_tested = 0;
-
- const services = element.all(by.css('cd-hosts a.service-link'));
- // check that text (links) is present in services box
- await expect(services.count()).toBeGreaterThan(0, 'No services links exist on first host');
-
- /**
- * Currently there is an issue [1] in ceph that it's causing
- * a random appearance of a mds service in the hosts service listing.
- * Decreasing the number of service by 1 temporarily fixes the e2e failure.
- *
- * TODO: Revert this change when the issue has been fixed.
- *
- * [1] https://tracker.ceph.com/issues/41538
- */
- const num_links = (await services.count()) - 1;
-
- for (let i = 0; i < num_links; i++) {
- // click link, check it worked by looking for changed breadcrumb,
- // navigate back to hosts page, repeat until all links checked
- await services.get(i).click();
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Performance Counters');
- await this.navigateBack();
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Hosts');
- links_tested++;
- }
- // check if any links were actually tested
- await expect(links_tested > 0).toBe(true, 'No links were tested. Test failed');
- }
-}
+++ /dev/null
-import { PoolPageHelper } from '../pools/pools.po';
-import { ConfigurationPageHelper } from './configuration.po';
-import { LogsPageHelper } from './logs.po';
-
-describe('Logs page', () => {
- let logs: LogsPageHelper;
- let pools: PoolPageHelper;
- let configuration: ConfigurationPageHelper;
-
- const poolname = 'logs_e2e_test_pool';
- const configname = 'log_graylog_port';
- const today = new Date();
- let hour = today.getHours();
- if (hour > 12) {
- hour = hour - 12;
- }
- const minute = today.getMinutes();
-
- beforeAll(() => {
- logs = new LogsPageHelper();
- pools = new PoolPageHelper();
- configuration = new ConfigurationPageHelper();
- });
-
- afterEach(async () => {
- await LogsPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- beforeAll(async () => {
- await logs.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await logs.waitTextToBePresent(logs.getBreadcrumb(), 'Logs');
- });
-
- it('should show two tabs', async () => {
- await expect(logs.getTabsCount()).toEqual(2);
- });
-
- it('should show cluster logs tab at first', async () => {
- await expect(logs.getTabText(0)).toEqual('Cluster Logs');
- });
-
- it('should show audit logs as a second tab', async () => {
- await expect(logs.getTabText(1)).toEqual('Audit Logs');
- });
- });
-
- describe('audit logs respond to pool creation and deletion test', () => {
- it('should create pool and check audit logs reacted', async () => {
- await pools.navigateTo('create');
- await pools.create(poolname, 8);
-
- await pools.navigateTo();
- await pools.exist(poolname, true);
-
- await logs.checkAuditForPoolFunction(poolname, 'create', hour, minute);
- });
-
- it('should delete pool and check audit logs reacted', async () => {
- await pools.navigateTo();
- await pools.delete(poolname);
-
- await logs.navigateTo();
- await logs.checkAuditForPoolFunction(poolname, 'delete', hour, minute);
- });
- });
-
- describe('audit logs respond to editing configuration setting test', () => {
- it('should change config settings and check audit logs reacted', async () => {
- await configuration.navigateTo();
- await configuration.edit(configname, ['global', '5']);
-
- await logs.navigateTo();
- await logs.checkAuditForConfigChange(configname, 'global', hour, minute);
-
- await configuration.navigateTo();
- await configuration.configClear(configname);
- });
- });
-});
+++ /dev/null
-import { $, $$, by, element, protractor } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class LogsPageHelper extends PageHelper {
- pages = { index: '/#/logs' };
-
- async checkAuditForPoolFunction(
- poolname: string,
- poolfunction: string,
- hour: number,
- minute: number
- ) {
- await this.navigateTo();
-
- // sometimes the modal from deleting pool is still present at this point.
- // This wait makes sure it isn't
- await this.waitStaleness(element(by.cssContainingText('.modal-dialog', 'Delete Pool')));
-
- // go to audit logs tab
- await element(by.cssContainingText('.nav-link', 'Audit Logs')).click();
-
- // Enter an earliest time so that no old messages with the same pool name show up
- await $$('.bs-timepicker-field')
- .get(0)
- .sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a'));
- await $$('.bs-timepicker-field').get(0).sendKeys(protractor.Key.BACK_SPACE);
- if (hour < 10) {
- await $$('.bs-timepicker-field').get(0).sendKeys('0');
- }
- await $$('.bs-timepicker-field').get(0).sendKeys(hour);
-
- await $$('.bs-timepicker-field')
- .get(1)
- .sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a'));
- await $$('.bs-timepicker-field').get(1).sendKeys(protractor.Key.BACK_SPACE);
- if (minute < 10) {
- await $$('.bs-timepicker-field').get(1).sendKeys('0');
- }
- await $$('.bs-timepicker-field').get(1).sendKeys(minute);
-
- // Enter the pool name into the filter box
- await $$('input.form-control.ng-valid').first().click();
- await $$('input.form-control.ng-valid').first().clear();
- await $$('input.form-control.ng-valid').first().sendKeys(poolname);
-
- const audit_logs_tab = $('.tab-pane.active');
- const audit_logs_body = audit_logs_tab.element(by.css('.card-body'));
- const logs = audit_logs_body.all(by.cssContainingText('.message', poolname));
-
- await expect(logs.getText()).toMatch(poolname);
- await expect(logs.getText()).toMatch(`pool ${poolfunction}`);
- }
-
- async checkAuditForConfigChange(
- configname: string,
- setting: string,
- hour: number,
- minute: number
- ) {
- await this.navigateTo();
-
- // go to audit logs tab
- await element(by.cssContainingText('.nav-link', 'Audit Logs')).click();
-
- // Enter an earliest time so that no old messages with the same config name show up
- await $$('.bs-timepicker-field')
- .get(0)
- .sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a'));
- await $$('.bs-timepicker-field').get(0).sendKeys(protractor.Key.BACK_SPACE);
- if (hour < 10) {
- await $$('.bs-timepicker-field').get(0).sendKeys('0');
- }
- await $$('.bs-timepicker-field').get(0).sendKeys(hour);
-
- await $$('.bs-timepicker-field')
- .get(1)
- .sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a'));
- await $$('.bs-timepicker-field').get(1).sendKeys(protractor.Key.BACK_SPACE);
- if (minute < 10) {
- await $$('.bs-timepicker-field').get(1).sendKeys('0');
- }
- await $$('.bs-timepicker-field').get(1).sendKeys(minute);
-
- // Enter the config name into the filter box
- await $$('input.form-control.ng-valid').first().click();
- await $$('input.form-control.ng-valid').first().clear();
- await $$('input.form-control.ng-valid').first().sendKeys(configname);
-
- const audit_logs_tab = $('.tab-pane.active');
- const audit_logs_body = audit_logs_tab.element(by.css('.card-body'));
- const logs = audit_logs_body.all(by.cssContainingText('.message', configname));
-
- await this.waitPresence(logs.first());
-
- await expect(logs.getText()).toMatch(configname);
- await expect(logs.getText()).toMatch(setting);
- }
-}
+++ /dev/null
-import { ManagerModulesPageHelper } from './mgr-modules.po';
-
-describe('Manager modules page', () => {
- let mgrmodules: ManagerModulesPageHelper;
-
- beforeAll(() => {
- mgrmodules = new ManagerModulesPageHelper();
- });
-
- afterEach(async () => {
- await ManagerModulesPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await mgrmodules.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await mgrmodules.waitTextToBePresent(mgrmodules.getBreadcrumb(), 'Manager modules');
- });
- });
-
- describe('verifies editing functionality for manager modules', () => {
- beforeAll(async () => {
- await mgrmodules.navigateTo();
- });
-
- it('should test editing on diskprediction_local module', async () => {
- const diskpredLocalArr = [
- ['11', 'predict_interval'],
- ['0122', 'sleep_interval']
- ];
- await mgrmodules.editMgrModule('diskprediction_local', diskpredLocalArr);
- });
-
- it('should test editing on balancer module', async () => {
- const balancerArr = [['rq', 'pool_ids']];
- await mgrmodules.editMgrModule('balancer', balancerArr);
- });
-
- it('should test editing on dashboard module', async () => {
- const dashboardArr = [
- ['rq', 'RGW_API_USER_ID'],
- ['rafa', 'GRAFANA_API_PASSWORD']
- ];
- await mgrmodules.editMgrModule('dashboard', dashboardArr);
- });
-
- it('should test editing on devicehealth module', async () => {
- await mgrmodules.editDevicehealth('1987', 'sox', '1999', '2020', '456', '567');
- });
- });
-});
+++ /dev/null
-import { $$, by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class ManagerModulesPageHelper extends PageHelper {
- pages = {
- index: '/#/mgr-modules'
- };
-
- // NOTABLE ISSUES: .clear() does not work on most text boxes, therefore using sendKeys
- // a Ctrl + 'a' BACK_SPACE is used.
- // The need to click the module repeatedly in the table is to ensure
- // that the values in the details tab updated. This fixed a bug I experienced.
-
- async editMgrModule(name: string, tuple: string[][]) {
- // Selects the Manager Module and then fills in the desired fields.
- // Doesn't check/uncheck boxes because it is not reflected in the details table.
- // DOES NOT WORK FOR ALL MGR MODULES, for example, Device health
- await this.navigateTo();
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
- await element(by.cssContainingText('button', 'Edit')).click();
-
- for (const entry of tuple) {
- // Clears fields and adds edits
- await this.clearInput(element(by.id(entry[1])));
- await element(by.id(entry[1])).sendKeys(entry[0]);
- }
-
- await element(by.cssContainingText('button', 'Update')).click();
- // Checks if edits appear
- await this.navigateTo();
- await this.waitVisibility(this.getFirstTableCellWithText(name));
- await this.getExpandCollapseElement(name).click();
- for (const entry of tuple) {
- await this.waitTextToBePresent($$('.datatable-body').last(), entry[0]);
- }
-
- // Clear mgr module of all edits made to it
- await this.navigateTo();
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
- await element(by.cssContainingText('button', 'Edit')).click();
-
- // Clears the editable fields
- for (const entry of tuple) {
- await this.clearInput(element(by.id(entry[1])));
- }
-
- // Checks that clearing represents in details tab of module
- await element(by.cssContainingText('button', 'Update')).click();
- await this.navigateTo();
- await this.waitVisibility(this.getFirstTableCellWithText(name));
- await this.getExpandCollapseElement(name).click();
- for (const entry of tuple) {
- await this.waitTextNotPresent($$('.datatable-body').last(), entry[0]);
- }
- }
-
- async editDevicehealth(
- threshhold?: string,
- pooln?: string,
- retention?: string,
- scrape?: string,
- sleep?: string,
- warn?: string
- ) {
- // Isn't called by editMgrModule since clearing doesn't work.
- // Selects the Devicehealth manager module, then fills in the desired fields, including all fields except
- // checkboxes. Clicking checkboxes has been a notable issue in Protractor, therefore they were omitted in this
- // version of the tests. Could be added in a future PR. Then checks if these edits appear in the details table.
- await this.navigateTo();
- let devHealthArray: [string, string][];
- devHealthArray = [
- [threshhold, 'mark_out_threshold'],
- [pooln, 'pool_name'],
- [retention, 'retention_period'],
- [scrape, 'scrape_frequency'],
- [sleep, 'sleep_interval'],
- [warn, 'warn_threshold']
- ];
-
- await this.waitClickableAndClick(this.getFirstTableCellWithText('devicehealth'));
- await element(by.cssContainingText('button', 'Edit')).click();
- for (let i = 0, devHealthTuple; (devHealthTuple = devHealthArray[i]); i++) {
- if (devHealthTuple[0] !== undefined) {
- // Clears and inputs edits
- await this.clearInput(element(by.id(devHealthTuple[1])));
- await element(by.id(devHealthTuple[1])).sendKeys(devHealthTuple[0]);
- }
- }
-
- await element(by.cssContainingText('button', 'Update')).click();
- await this.navigateTo();
- await this.waitVisibility(this.getFirstTableCellWithText('devicehealth'));
- // Checks for visibility of devicehealth in table
- await this.getExpandCollapseElement('devicehealth').click();
- for (let i = 0, devHealthTuple: [string, string]; (devHealthTuple = devHealthArray[i]); i++) {
- if (devHealthTuple[0] !== undefined) {
- await this.waitFn(async () => {
- // Repeatedly reclicks the module to check if edits has been done
- await element(by.cssContainingText('.datatable-body-cell-label', 'devicehealth')).click();
- return this.waitTextToBePresent($$('.datatable-body').last(), devHealthTuple[0]);
- });
- }
- }
-
- // Inputs old values into devicehealth fields. This manager module doesnt allow for updates
- // to be made when the values are cleared. Therefore, I restored them to their original values
- // (on my local run of ceph-dev, this is subject to change i would assume). I'd imagine there is a
- // better way of doing this.
- await this.navigateTo();
- await this.waitClickableAndClick(this.getFirstTableCellWithText('devicehealth'));
- await element(by.cssContainingText('button', 'Edit')).click();
- await this.clearInput(element(by.id('mark_out_threshold')));
- await element(by.id('mark_out_threshold')).sendKeys('2419200');
-
- await this.clearInput(element(by.id('pool_name')));
- await element(by.id('pool_name')).sendKeys('device_health_metrics');
-
- await this.clearInput(element(by.id('retention_period')));
- await element(by.id('retention_period')).sendKeys('15552000');
-
- await this.clearInput(element(by.id('scrape_frequency')));
- await element(by.id('scrape_frequency')).sendKeys('86400');
-
- await this.clearInput(element(by.id('sleep_interval')));
- await element(by.id('sleep_interval')).sendKeys('600');
-
- await this.clearInput(element(by.id('warn_threshold')));
- await element(by.id('warn_threshold')).sendKeys('7257600');
-
- // Checks that clearing represents in details tab
- await this.waitClickableAndClick(element(by.cssContainingText('button', 'Update')));
- await this.navigateTo();
- await this.waitVisibility(this.getFirstTableCellWithText('devicehealth'));
- await this.getExpandCollapseElement('devicehealth').click();
- for (let i = 0, devHealthTuple: [string, string]; (devHealthTuple = devHealthArray[i]); i++) {
- if (devHealthTuple[0] !== undefined) {
- await this.waitFn(async () => {
- // Repeatedly reclicks the module to check if clearing has been done
- await element(by.cssContainingText('.datatable-body-cell-label', 'devicehealth')).click();
- return this.waitTextNotPresent($$('.datatable-body').last(), devHealthTuple[0]);
- });
- }
- }
- }
-}
+++ /dev/null
-import { MonitorsPageHelper } from './monitors.po';
-
-describe('Monitors page', () => {
- let monitors: MonitorsPageHelper;
-
- beforeAll(() => {
- monitors = new MonitorsPageHelper();
- });
-
- afterEach(async () => {
- await MonitorsPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await monitors.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await monitors.waitTextToBePresent(monitors.getBreadcrumb(), 'Monitors');
- });
- });
-
- describe('fields check', () => {
- beforeAll(async () => {
- await monitors.navigateTo();
- });
-
- it('should check status table is present', async () => {
- // check for table header 'Status'
- await expect(monitors.getLegends().get(0).getText()).toMatch('Status');
-
- // check for fields in table
- await expect(monitors.getStatusTables().getText()).toMatch('Cluster ID');
- await expect(monitors.getStatusTables().getText()).toMatch('monmap modified');
- await expect(monitors.getStatusTables().getText()).toMatch('monmap epoch');
- await expect(monitors.getStatusTables().getText()).toMatch('quorum con');
- await expect(monitors.getStatusTables().getText()).toMatch('quorum mon');
- await expect(monitors.getStatusTables().getText()).toMatch('required con');
- await expect(monitors.getStatusTables().getText()).toMatch('required mon');
- });
-
- it('should check In Quorum and Not In Quorum tables are present', async () => {
- // check for there to be two tables
- await expect(monitors.getDataTables().count()).toEqual(2);
-
- // check for table header 'In Quorum'
- await expect(monitors.getLegends().get(1).getText()).toMatch('In Quorum');
-
- // check for table header 'Not In Quorum'
- await expect(monitors.getLegends().get(2).getText()).toMatch('Not In Quorum');
-
- // verify correct columns on In Quorum table
- await expect(monitors.getDataTableHeaders().get(0).getText()).toMatch('Name');
- await expect(monitors.getDataTableHeaders().get(0).getText()).toMatch('Rank');
- await expect(monitors.getDataTableHeaders().get(0).getText()).toMatch('Public Address');
- await expect(monitors.getDataTableHeaders().get(0).getText()).toMatch('Open Sessions');
-
- // verify correct columns on Not In Quorum table
- await expect(monitors.getDataTableHeaders().get(1).getText()).toMatch('Name');
- await expect(monitors.getDataTableHeaders().get(1).getText()).toMatch('Rank');
- await expect(monitors.getDataTableHeaders().get(1).getText()).toMatch('Public Address');
- });
- });
-});
+++ /dev/null
-import { PageHelper } from '../page-helper.po';
-
-export class MonitorsPageHelper extends PageHelper {
- pages = { index: '/#/monitor' };
-}
+++ /dev/null
-import { $$, by, element } from 'protractor';
-import { OSDsPageHelper } from './osds.po';
-
-describe('OSDs page', () => {
- let osds: OSDsPageHelper;
-
- beforeAll(async () => {
- osds = new OSDsPageHelper();
- await osds.navigateTo();
- });
-
- afterEach(async () => {
- await OSDsPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- it('should open and show breadcrumb', async () => {
- await osds.waitTextToBePresent(osds.getBreadcrumb(), 'OSDs');
- });
-
- it('should show two tabs', async () => {
- await expect(osds.getTabsCount()).toEqual(2);
- });
-
- it('should show OSDs list tab at first', async () => {
- await expect(osds.getTabText(0)).toEqual('OSDs List');
- });
-
- it('should show overall performance as a second tab', async () => {
- await expect(osds.getTabText(1)).toEqual('Overall Performance');
- });
- });
-
- describe('check existence of fields on OSD page', () => {
- it('should check that number of rows and count in footer match', async () => {
- await expect(osds.getTableTotalCount()).toEqual(osds.getTableRows().count());
- });
-
- it('should verify that buttons exist', async () => {
- await expect(element(by.cssContainingText('button', 'Create')).isPresent()).toBe(true);
- await expect(
- element(by.cssContainingText('button', 'Cluster-wide configuration')).isPresent()
- ).toBe(true);
- });
-
- describe('by selecting one row in OSDs List', () => {
- beforeAll(async () => {
- await osds.waitClickableAndClick(osds.getFirstExpandCollapseElement());
- });
-
- it('should verify that selected footer increases', async () => {
- await expect(osds.getTableSelectedCount()).toEqual(1);
- });
-
- it('should show the correct text for the tab labels', async () => {
- const tabHeadings = $$('#tabset-osd-details > div > tab').map((e) =>
- e.getAttribute('heading')
- );
- await expect(tabHeadings).toEqual([
- 'Devices',
- 'Attributes (OSD map)',
- 'Metadata',
- 'Device health',
- 'Performance counter',
- 'Histogram',
- 'Performance Details'
- ]);
- });
- });
- });
-});
+++ /dev/null
-import { PageHelper } from '../page-helper.po';
-
-export class OSDsPageHelper extends PageHelper {
- pages = { index: '/#/osd' };
-}
+++ /dev/null
-import { FilesystemsPageHelper } from './filesystems.po';
-
-describe('Filesystems page', () => {
- let filesystems: FilesystemsPageHelper;
-
- beforeAll(() => {
- filesystems = new FilesystemsPageHelper();
- });
-
- afterEach(async () => {
- await FilesystemsPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await filesystems.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await filesystems.waitTextToBePresent(filesystems.getBreadcrumb(), 'Filesystems');
- });
- });
-});
+++ /dev/null
-import { PageHelper } from '../page-helper.po';
-
-export class FilesystemsPageHelper extends PageHelper {
- pages = { index: '/#/cephfs' };
-}
+++ /dev/null
-import { NfsPageHelper } from './nfs.po';
-
-describe('Nfs page', () => {
- let nfs: NfsPageHelper;
-
- beforeAll(() => {
- nfs = new NfsPageHelper();
- });
-
- afterEach(async () => {
- await NfsPageHelper.checkConsole();
- });
-
- describe('breadcrumb test', () => {
- beforeAll(async () => {
- await nfs.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await nfs.waitTextToBePresent(nfs.getBreadcrumb(), 'NFS');
- });
- });
-});
+++ /dev/null
-import { PageHelper } from '../page-helper.po';
-
-export class NfsPageHelper extends PageHelper {
- pages = { index: '/#/nfs' };
-}
+++ /dev/null
-import {
- $,
- $$,
- browser,
- by,
- element,
- ElementArrayFinder,
- ElementFinder,
- protractor
-} from 'protractor';
-
-const EC = browser.ExpectedConditions;
-const TIMEOUT = 20000;
-
-interface Pages {
- index: string;
-}
-
-export abstract class PageHelper {
- pages: Pages;
-
- /**
- * Checks if there are any errors on the browser
- *
- * @static
- * @memberof Helper
- */
- static async checkConsole() {
- let browserLog = await browser.manage().logs().get('browser');
-
- browserLog = browserLog.filter((log) => log.level.value > 900);
-
- if (browserLog.length > 0) {
- console.log('\n log: ' + require('util').inspect(browserLog));
- }
-
- await expect(browserLog.length).toEqual(0);
- }
-
- /**
- * Decorator to be used on Helper methods to restrict access to one particular URL. This shall
- * help developers to prevent and highlight mistakes. It also reduces boilerplate code and by
- * thus, increases readability.
- */
- static restrictTo(page: string): Function {
- return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
- const fn: Function = descriptor.value;
- descriptor.value = function (...args: any) {
- return browser
- .getCurrentUrl()
- .then((url) =>
- url.endsWith(page)
- ? fn.apply(this, args)
- : Promise.reject(
- `Method ${target.constructor.name}::${propertyKey} is supposed to be ` +
- `run on path "${page}", but was run on URL "${url}"`
- )
- );
- };
- };
- }
-
- /**
- * Get the active breadcrumb item.
- */
- getBreadcrumb(): ElementFinder {
- return $('.breadcrumb-item.active');
- }
-
- async getTabText(index: number): Promise<string> {
- return $$('.nav.nav-tabs li').get(index).getText();
- }
-
- async getTableTotalCount(): Promise<number> {
- const text = await $$('.datatable-footer-inner .page-count span')
- .filter(async (e) => (await e.getText()).includes('total'))
- .first()
- .getText();
- return Number(text.match(/(\d+)\s+total/)[1]);
- }
-
- async getTableSelectedCount(): Promise<number> {
- const text = await $$('.datatable-footer-inner .page-count span')
- .filter(async (e) => (await e.getText()).includes('selected'))
- .first()
- .getText();
- return Number(text.match(/(\d+)\s+selected/)[1]);
- }
-
- async getTableFoundCount(): Promise<number> {
- const text = await $$('.datatable-footer-inner .page-count span')
- .filter(async (e) => (await e.getText()).includes('found'))
- .first()
- .getText();
- return Number(text.match(/(\d+)\s+found/)[1]);
- }
-
- getFirstTableCellWithText(content: string): ElementFinder {
- return element.all(by.cssContainingText('.datatable-body-cell-label', content)).first();
- }
-
- getFirstExpandCollapseElement(): ElementFinder {
- return element.all(by.className('tc_expand-collapse')).first();
- }
-
- getExpandCollapseElement(content: string): ElementFinder {
- const tableRow = element(by.cssContainingText('.datatable-body-row', content));
- return tableRow.element(by.className('tc_expand-collapse'));
- }
-
- getTableRow(content: string) {
- return element(by.cssContainingText('.datatable-body-row', content));
- }
-
- getTable(): ElementFinder {
- return $('.datatable-body');
- }
-
- async getTabsCount(): Promise<number> {
- return $$('.nav.nav-tabs li').count();
- }
-
- /**
- * Ceph Dashboards' <input type="checkbox"> tag is not visible. Instead of the real checkbox, a
- * replacement is shown which is supposed to have an adapted style. The replacement checkbox shown
- * is part of the label and is rendered in the "::before" pseudo element of the label, hence the
- * label is always clicked when the user clicks the replacement checkbox.
- *
- * This method finds corresponding label to the given checkbox and clicks it instead of the (fake)
- * checkbox, like it is the case with real users.
- *
- * Alternatively, the checkbox' label can be passed.
- *
- * @param elem The checkbox or corresponding label
- */
- async clickCheckbox(elem: ElementFinder): Promise<void> {
- const tagName = await elem.getTagName();
- let label: ElementFinder = null; // Both types are clickable
-
- await this.waitPresence(elem);
- if (tagName === 'input') {
- if ((await elem.getAttribute('type')) === 'checkbox') {
- label = elem.element(by.xpath('..')).$(`label[for="${await elem.getAttribute('id')}"]`);
- } else {
- return Promise.reject('element <input> must be of type checkbox');
- }
- } else if (tagName === 'label') {
- label = elem;
- } else {
- return Promise.reject(
- `element <${tagName}> is not of the correct type. You need to pass a checkbox or label`
- );
- }
-
- return this.waitClickableAndClick(label);
- }
-
- /**
- * Helper method to select an option inside a select element.
- * This method will also expect that the option was set.
- * @param option The option text (not value) to be selected.
- */
- async selectOption(selectionName: string, option: string) {
- await element(by.cssContainingText(`select[name=${selectionName}] option`, option)).click();
- return this.expectSelectOption(selectionName, option);
- }
-
- /**
- * Helper method to expect a set option inside a select element.
- * @param option The selected option text (not value) that is to
- * be expected.
- */
- async expectSelectOption(selectionName: string, option: string) {
- return expect(
- element(by.css(`select[name=${selectionName}] option:checked`)).getText()
- ).toContain(option);
- }
-
- /**
- * Returns the cell with the content given in `content`. Will not return a rejected Promise if the
- * table cell hasn't been found. It behaves this way to enable to wait for
- * visibility/invisibility/presence of the returned element.
- *
- * It will return a rejected Promise if the result is ambiguous, though. That means if the search
- * for content has been completed, but more than a single row is shown in the data table.
- */
- async getTableCellByContent(content: string): Promise<ElementFinder> {
- const searchInput = $('#pool-list > div .search input');
- const rowAmountInput = $('#pool-list > div > div > .dataTables_paginate input');
- const footer = $('#pool-list > div datatable-footer');
-
- await rowAmountInput.clear();
- await rowAmountInput.sendKeys('10');
- await searchInput.clear();
- await searchInput.sendKeys(content);
-
- const count = Number(await footer.getAttribute('ng-reflect-row-count'));
- if (count !== 0 && count > 1) {
- return Promise.reject('getTableCellByContent: Result is ambiguous');
- } else {
- return Promise.resolve(
- element(
- by.cssContainingText('.datatable-body-cell-label', new RegExp(`^\\s${content}\\s$`))
- )
- );
- }
- }
-
- /**
- * Used when .clear() does not work on a text box, sends a Ctrl + a, BACKSPACE
- */
- async clearInput(elem: ElementFinder) {
- const types = ['text', 'number'];
- if ((await elem.getTagName()) === 'input' && types.includes(await elem.getAttribute('type'))) {
- return await elem.sendKeys(
- protractor.Key.chord(protractor.Key.CONTROL, 'a'),
- protractor.Key.BACK_SPACE
- );
- } else {
- return Promise.reject(`Element ${elem} does not match the expected criteria.`);
- }
- }
-
- async navigateTo(page: string = null) {
- page = page || 'index';
- const url = this.pages[page];
- await browser.get(url);
- }
-
- async navigateBack() {
- await browser.navigate().back();
- }
-
- getDataTables(): ElementArrayFinder {
- return $$('cd-table');
- }
-
- /**
- * Gets column headers of table
- */
- getDataTableHeaders(): ElementArrayFinder {
- return $$('.datatable-header');
- }
-
- /**
- * Grabs striped tables
- */
- getStatusTables(): ElementArrayFinder {
- return $$('.table.table-striped');
- }
-
- /**
- * Grabs legends above tables
- */
- getLegends(): ElementArrayFinder {
- return $$('legend');
- }
-
- getToast() {
- return $('.ngx-toastr');
- }
-
- async waitPresence(elem: ElementFinder, message?: string) {
- return browser.wait(EC.presenceOf(elem), TIMEOUT, message);
- }
-
- async waitStaleness(elem: ElementFinder, message?: string) {
- return browser.wait(EC.stalenessOf(elem), TIMEOUT, message);
- }
-
- /**
- * This method will wait for the element to be clickable and then click it.
- */
- async waitClickableAndClick(elem: ElementFinder, message?: string) {
- await browser.wait(EC.elementToBeClickable(elem), TIMEOUT, message);
- return elem.click();
- }
-
- async waitVisibility(elem: ElementFinder, message?: string) {
- return browser.wait(EC.visibilityOf(elem), TIMEOUT, message);
- }
-
- async waitInvisibility(elem: ElementFinder, message?: string) {
- return browser.wait(EC.invisibilityOf(elem), TIMEOUT, message);
- }
-
- async waitTextToBePresent(elem: ElementFinder, text: string, message?: string) {
- return browser.wait(EC.textToBePresentInElement(elem, text), TIMEOUT, message);
- }
-
- async waitTextNotPresent(elem: ElementFinder, text: string, message?: string) {
- return browser.wait(EC.not(EC.textToBePresentInElement(elem, text)), TIMEOUT, message);
- }
-
- async waitFn(func: Function, message?: string, timeout: number = TIMEOUT) {
- return browser.wait(func, timeout, message);
- }
-
- getFirstCell(): ElementFinder {
- return $$('.datatable-body-cell-label').first();
- }
-
- /**
- * This is a generic method to delete table rows.
- * It will select the first row that contains the provided name and delete it.
- * After that it will wait until the row is no longer displayed.
- */
- async delete(name: string): Promise<any> {
- // Selects row
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name));
-
- // Clicks on table Delete button
- await $$('.table-actions button.dropdown-toggle').first().click(); // open submenu
- await $('li.delete a').click(); // click on "delete" menu item
-
- // Confirms deletion
- await this.clickCheckbox($('.custom-control-label'));
- await element(by.cssContainingText('button', 'Delete')).click();
-
- // Waits for item to be removed from table
- return this.waitStaleness(this.getFirstTableCellWithText(name));
- }
-
- getTableRows() {
- return $$('datatable-row-wrapper');
- }
-
- /**
- * Uncheck all checked table rows.
- */
- async uncheckAllTableRows() {
- await $$(
- '.datatable-body-cell-label .datatable-checkbox input[type=checkbox]:checked'
- ).each((e: ElementFinder) => e.click());
- }
-
- async filterTable(name: string, option: string) {
- await this.waitClickableAndClick($('.tc_filter_name > a'));
- await element(by.cssContainingText(`.tc_filter_name .dropdown-item`, name)).click();
-
- await this.waitClickableAndClick($('.tc_filter_option > a'));
- await element(by.cssContainingText(`.tc_filter_option .dropdown-item`, option)).click();
- }
-
- async clearTableSearchInput() {
- return this.waitClickableAndClick($('cd-table .search button'));
- }
-}
+++ /dev/null
-import { PoolPageHelper } from './pools.po';
-
-describe('Pools page', () => {
- let pools: PoolPageHelper;
- const poolName = 'pool_e2e_pool/test';
-
- beforeAll(async () => {
- pools = new PoolPageHelper();
- await pools.navigateTo();
- });
-
- afterEach(async () => {
- await PoolPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- it('should open and show breadcrumb', async () => {
- await pools.waitTextToBePresent(pools.getBreadcrumb(), 'Pools');
- });
-
- it('should show two tabs', async () => {
- await expect(pools.getTabsCount()).toEqual(2);
- });
-
- it('should show pools list tab at first', async () => {
- await expect(pools.getTabText(0)).toEqual('Pools List');
- });
-
- it('should show overall performance as a second tab', async () => {
- await expect(pools.getTabText(1)).toEqual('Overall Performance');
- });
- });
-
- it('should create a pool', async () => {
- await pools.exist(poolName, false);
- await pools.navigateTo('create');
- await pools.create(poolName, 8);
- await pools.navigateTo();
- await pools.exist(poolName, true);
- });
-
- it('should edit a pools placement group', async () => {
- await pools.exist(poolName, true);
- await pools.navigateTo();
- await pools.edit_pool_pg(poolName, 32);
- });
-
- it('should delete a pool', async () => {
- await pools.navigateTo();
- await pools.delete(poolName);
- });
-});
+++ /dev/null
-import { $, by, element, protractor } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-const pages = {
- index: '/#/pool',
- create: '/#/pool/create'
-};
-
-export class PoolPageHelper extends PageHelper {
- pages = pages;
-
- private isPowerOf2(n: number): boolean {
- // tslint:disable-next-line: no-bitwise
- return (n & (n - 1)) === 0;
- }
-
- @PageHelper.restrictTo(pages.index)
- async exist(name: string, oughtToBePresent = true) {
- const tableCell = await this.getTableCellByContent(name);
- const waitFn = oughtToBePresent ? this.waitVisibility : this.waitInvisibility;
- try {
- await waitFn(tableCell);
- } catch (e) {
- const visibility = oughtToBePresent ? 'invisible' : 'visible';
- const msg = `Pool "${name}" is ${visibility}, but should not be. Waiting for a change timed out`;
- return Promise.reject(msg);
- }
- return Promise.resolve();
- }
-
- @PageHelper.restrictTo(pages.create)
- async create(name: string, placement_groups: number, ...apps: string[]): Promise<any> {
- const nameInput = $('input[name=name]');
- await nameInput.clear();
- if (!this.isPowerOf2(placement_groups)) {
- return Promise.reject(`Placement groups ${placement_groups} are not a power of 2`);
- }
- await nameInput.sendKeys(name);
- await this.selectOption('poolType', 'replicated');
-
- await this.expectSelectOption('pgAutoscaleMode', 'on');
- await this.selectOption('pgAutoscaleMode', 'off'); // To show pgNum field
- await $('input[name=pgNum]').sendKeys(
- protractor.Key.CONTROL,
- 'a',
- protractor.Key.NULL,
- placement_groups
- );
- await this.setApplications(apps);
- await element(by.css('cd-submit-button')).click();
-
- return Promise.resolve();
- }
-
- async edit_pool_pg(name: string, new_pg: number, wait = true): Promise<void> {
- if (!this.isPowerOf2(new_pg)) {
- return Promise.reject(`Placement groups ${new_pg} are not a power of 2`);
- }
- const elem = await this.getTableCellByContent(name);
- await this.waitClickableAndClick(elem); // select pool from the table
- await element(by.cssContainingText('button', 'Edit')).click(); // click edit button
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit'); // verify we are now on edit page
- await $('input[name=pgNum]').sendKeys(protractor.Key.CONTROL, 'a', protractor.Key.NULL, new_pg);
- await element(by.css('cd-submit-button')).click();
- const str = `${new_pg} active+clean`;
- await this.waitVisibility(this.getTableRow(name), 'Timed out waiting for table row to load');
- if (wait) {
- await this.waitTextToBePresent(
- this.getTableRow(name),
- str,
- 'Timed out waiting for placement group to be updated'
- );
- }
- }
-
- private async setApplications(apps: string[]) {
- if (!apps || apps.length === 0) {
- return;
- }
- await element(by.css('.float-left.mr-2.select-menu-edit')).click();
- await this.waitVisibility(element(by.css('.popover-content.popover-body')));
- apps.forEach(
- async (app) => await element(by.cssContainingText('.select-menu-item-content', app)).click()
- );
- }
-}
+++ /dev/null
-import { $ } from 'protractor';
-import { BucketsPageHelper } from './buckets.po';
-
-describe('RGW buckets page', () => {
- let buckets: BucketsPageHelper;
- const bucket_name = '000test';
-
- beforeAll(async () => {
- buckets = new BucketsPageHelper();
- });
-
- afterEach(async () => {
- await BucketsPageHelper.checkConsole();
- });
-
- describe('breadcrumb tests', () => {
- beforeEach(async () => {
- await buckets.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await expect($('.breadcrumb-item.active').getText()).toBe('Buckets');
- });
- });
-
- describe('create, edit & delete bucket tests', () => {
- beforeEach(async () => {
- await buckets.navigateTo();
- await buckets.uncheckAllTableRows();
- });
-
- it('should create bucket', async () => {
- await buckets.navigateTo('create');
- await buckets.create(
- bucket_name,
- '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
- 'default-placement'
- );
- await expect(buckets.getFirstTableCellWithText(bucket_name).isPresent()).toBe(true);
- });
-
- it('should edit bucket', async () => {
- await buckets.edit(bucket_name, 'dev');
- await expect(buckets.getTable().getText()).toMatch('dev');
- });
-
- it('should delete bucket', async () => {
- await buckets.delete(bucket_name);
- });
- });
-
- describe('Invalid Input in Create and Edit tests', () => {
- it('should test invalid inputs in create fields', async () => {
- await buckets.testInvalidCreate();
- });
-
- it('should test invalid input in edit owner field', async () => {
- await buckets.navigateTo('create');
- await buckets.create(
- '000rq',
- '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
- 'default-placement'
- );
- await buckets.testInvalidEdit('000rq');
- await buckets.navigateTo();
- await buckets.uncheckAllTableRows();
- await buckets.delete('000rq');
- });
- });
-});
+++ /dev/null
-import { by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-const pages = {
- index: '/#/rgw/bucket',
- create: '/#/rgw/bucket/create'
-};
-
-export class BucketsPageHelper extends PageHelper {
- pages = pages;
- versioningStateEnabled = 'Enabled';
- versioningStateSuspended = 'Suspended';
-
- private async selectOwner(owner: string) {
- return this.selectOption('owner', owner);
- }
-
- private async selectPlacementTarget(placementTarget: string) {
- return this.selectOption('placement-target', placementTarget);
- }
-
- /**
- * TODO add check to verify the existance of the bucket!
- * TODO let it print a meaningful error message (for devs) if it does not exist!
- */
- @PageHelper.restrictTo(pages.create)
- async create(name: string, owner: string, placementTarget: string) {
- // Enter in bucket name
- await element(by.id('bid')).sendKeys(name);
-
- // Select bucket owner
- await this.selectOwner(owner);
- await expect(element(by.id('owner')).getAttribute('class')).toContain('ng-valid');
-
- // Select bucket placement target:
- await this.selectPlacementTarget(placementTarget);
- await expect(element(by.id('placement-target')).getAttribute('class')).toContain('ng-valid');
-
- // Click the create button and wait for bucket to be made
- const createButton = element(by.cssContainingText('button', 'Create Bucket'));
- await createButton.click();
-
- return this.waitPresence(
- this.getFirstTableCellWithText(name),
- 'Timed out waiting for bucket creation'
- );
- }
-
- @PageHelper.restrictTo(pages.index)
- async edit(name: string, new_owner: string) {
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // wait for table to load and click
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
- await expect(element(by.css('input[name=placement-target]')).getAttribute('value')).toBe(
- 'default-placement'
- );
- await this.selectOwner(new_owner);
-
- // Enable versioning
- await expect(element(by.css('input[id=versioning]')).getAttribute('checked')).toBeFalsy();
- await element(by.css('label[for=versioning]')).click();
- await expect(element(by.css('input[id=versioning]')).getAttribute('checked')).toBeTruthy();
-
- await element(by.cssContainingText('button', 'Edit Bucket')).click();
-
- // wait to be back on buckets page with table visible and click
- await this.waitClickableAndClick(
- this.getExpandCollapseElement(name),
- 'Could not return to buckets page and load table after editing bucket'
- );
-
- // check its details table for edited owner field
- let bucketDataTable = element.all(by.css('.table.table-striped.table-bordered')).first();
- await expect(bucketDataTable.getText()).toMatch(new_owner);
-
- // Check versioning enabled:
- const ownerValueCell = bucketDataTable.all(by.css('tr')).get(2).all(by.css('td')).last();
- await expect(ownerValueCell.getText()).toEqual(new_owner);
- let versioningValueCell = bucketDataTable.all(by.css('tr')).get(11).all(by.css('td')).last();
- await expect(versioningValueCell.getText()).toEqual(this.versioningStateEnabled);
-
- // Disable versioning:
- await this.uncheckAllTableRows();
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // wait for table to load and click
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
- await element(by.css('label[for=versioning]')).click();
- await expect(element(by.css('input[id=versioning]')).getAttribute('checked')).toBeFalsy();
- await element(by.cssContainingText('button', 'Edit Bucket')).click();
-
- // Check versioning suspended:
- await this.waitClickableAndClick(
- this.getExpandCollapseElement(name),
- 'Could not return to buckets page and load table after editing bucket'
- );
- bucketDataTable = element.all(by.css('.table.table-striped.table-bordered')).first();
- versioningValueCell = bucketDataTable.all(by.css('tr')).get(11).all(by.css('td')).last();
- return expect(versioningValueCell.getText()).toEqual(this.versioningStateSuspended);
- }
-
- async testInvalidCreate() {
- await this.navigateTo('create');
- const nameInputField = element(by.id('bid')); // Grabs name box field
-
- // Gives an invalid name (too short), then waits for dashboard to determine validity
- await nameInputField.sendKeys('rq');
-
- await element(by.id('owner')).click(); // To trigger a validation
-
- await this.waitFn(async () => {
- // Waiting for website to decide if name is valid or not
- const klass = await nameInputField.getAttribute('class');
- return !klass.includes('ng-pending');
- }, 'Timed out waiting for dashboard to decide bucket name validity');
-
- // Check that name input field was marked invalid in the css
- await expect(nameInputField.getAttribute('class')).toContain('ng-invalid');
-
- // Check that error message was printed under name input field
- await expect(element(by.css('#bid + .invalid-feedback')).getText()).toMatch(
- 'The value is not valid.'
- );
-
- // Test invalid owner input
- // select some valid option. The owner drop down error message will not appear unless a valid user was selected at
- // one point before the invalid placeholder user is selected.
- await this.selectOwner('dev');
-
- // select the first option, which is invalid because it is a placeholder
- await this.selectOwner('Select a user');
-
- await nameInputField.click();
-
- // Check that owner drop down field was marked invalid in the css
- await expect(element(by.id('owner')).getAttribute('class')).toContain('ng-invalid');
-
- // Check that error message was printed under owner drop down field
- await expect(element(by.css('#owner + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- // Check invalid placement target input
- await this.selectOwner('dev');
- // The drop down error message will not appear unless a valid option is previsously selected.
- await this.selectPlacementTarget('default-placement');
- await this.selectPlacementTarget('Select a placement target');
- await nameInputField.click(); // Trigger validation
- await expect(element(by.id('placement-target')).getAttribute('class')).toContain('ng-invalid');
- await expect(element(by.css('#placement-target + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- // Clicks the Create Bucket button but the page doesn't move. Done by testing
- // for the breadcrumb
- await element(by.cssContainingText('button', 'Create Bucket')).click(); // Clicks Create Bucket button
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Create');
- // content in fields seems to subsist through tests if not cleared, so it is cleared
- await nameInputField.clear();
- return element(by.cssContainingText('button', 'Cancel')).click();
- }
-
- async testInvalidEdit(name: string) {
- await this.navigateTo();
-
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // wait for table to load and click
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
-
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
-
- await expect(element(by.css('input[id=versioning]')).getAttribute('checked')).toBeFalsy();
-
- // Chooses 'Select a user' rather than a valid owner on Edit Bucket page
- // and checks if it's an invalid input
-
- // select the first option, which is invalid because it is a placeholder
- await this.selectOwner('Select a user');
-
- // Changes when updated to bootstrap 4 -> Error message takes a long time to appear unless another field
- // is clicked on. For that reason, I'm having the test click on the edit button before checking for errors
- await element(by.cssContainingText('button', 'Edit Bucket')).click();
-
- // Check that owner drop down field was marked invalid in the css
- await expect(element(by.id('owner')).getAttribute('class')).toContain('ng-invalid');
-
- // Check that error message was printed under owner drop down field
- await expect(element(by.css('#owner + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
- }
-}
+++ /dev/null
-import { DaemonsPageHelper } from './daemons.po';
-
-describe('RGW daemons page', () => {
- let daemons: DaemonsPageHelper;
-
- beforeAll(() => {
- daemons = new DaemonsPageHelper();
- });
-
- afterEach(async () => {
- await DaemonsPageHelper.checkConsole();
- });
-
- describe('breadcrumb and tab tests', () => {
- beforeAll(async () => {
- await daemons.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await daemons.waitTextToBePresent(daemons.getBreadcrumb(), 'Daemons');
- });
-
- it('should show two tabs', async () => {
- await expect(daemons.getTabsCount()).toEqual(2);
- });
-
- it('should show daemons list tab at first', async () => {
- await expect(daemons.getTabText(0)).toEqual('Daemons List');
- });
-
- it('should show overall performance as a second tab', async () => {
- await expect(daemons.getTabText(1)).toEqual('Overall Performance');
- });
- });
-
- describe('details and performance counters table tests', () => {
- beforeAll(async () => {
- await daemons.navigateTo();
- });
-
- it('should check that details/performance tables are visible when daemon is selected', async () => {
- await daemons.checkTables();
- });
- });
-});
+++ /dev/null
-import { $$, by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class DaemonsPageHelper extends PageHelper {
- pages = { index: '/#/rgw/daemon' };
-
- async checkTables() {
- await this.navigateTo();
-
- // click on a daemon so details table appears
- await $$('.datatable-body-cell-label').first().click();
-
- const tab_container = $$('.tab-container').last();
- const details_table = tab_container.all(by.css('cd-table')).get(0);
- const performance_counters_table = tab_container.all(by.css('cd-table')).get(1);
-
- // check details table is visible
- await expect(details_table.isDisplayed()).toBe(true);
- // check at least one field is present
- await expect(details_table.getText()).toMatch('ceph_version');
- // check performance counters table is not currently visible
- await expect(performance_counters_table.isDisplayed()).toBe(false);
-
- // click on performance counters tab and check table is loaded
- await element(by.cssContainingText('.nav-link', 'Performance Counters')).click();
- await expect(performance_counters_table.isDisplayed()).toBe(true);
- // check at least one field is present
- await expect(performance_counters_table.getText()).toMatch('objecter.op_r');
- // check details table is not currently visible
- await expect(details_table.isDisplayed()).toBe(false);
-
- // click on performance details tab
- await element(by.cssContainingText('.nav-link', 'Performance Details')).click();
- // checks the other tabs' content isn't visible
- await expect(details_table.isDisplayed()).toBe(false);
- await expect(performance_counters_table.isDisplayed()).toBe(false);
- // TODO: Expect Grafana iFrame
- }
-}
+++ /dev/null
-import { UsersPageHelper } from './users.po';
-
-describe('RGW users page', () => {
- let users: UsersPageHelper;
- const user_name = '000user_create_edit_delete';
-
- beforeAll(() => {
- users = new UsersPageHelper();
- });
-
- afterEach(async () => {
- await UsersPageHelper.checkConsole();
- });
-
- describe('breadcrumb tests', () => {
- beforeEach(async () => {
- await users.navigateTo();
- });
-
- it('should open and show breadcrumb', async () => {
- await users.waitTextToBePresent(users.getBreadcrumb(), 'Users');
- });
- });
-
- describe('create, edit & delete user tests', () => {
- beforeEach(async () => {
- await users.navigateTo();
- await users.uncheckAllTableRows();
- });
-
- it('should create user', async () => {
- await users.navigateTo('create');
- await users.create(user_name, 'Some Name', 'original@website.com', '1200');
- await expect(users.getFirstTableCellWithText(user_name).isPresent()).toBe(true);
- });
-
- it('should edit users full name, email and max buckets', async () => {
- await users.edit(user_name, 'Another Identity', 'changed@othersite.com', '1969');
- });
-
- it('should delete user', async () => {
- await users.delete(user_name);
- });
- });
-
- describe('Invalid input tests', () => {
- it('should put invalid input into user creation form and check fields are marked invalid', async () => {
- await users.invalidCreate();
- });
-
- it('should put invalid input into user edit form and check fields are marked invalid', async () => {
- await users.invalidEdit();
- });
- });
-});
+++ /dev/null
-import { $, by, element } from 'protractor';
-import { protractor } from 'protractor/built/ptor';
-import { PageHelper } from '../page-helper.po';
-
-const pages = {
- index: '/#/rgw/user',
- create: '/#/rgw/user/create'
-};
-
-export class UsersPageHelper extends PageHelper {
- pages = pages;
-
- @PageHelper.restrictTo(pages.create)
- async create(username: string, fullname: string, email: string, maxbuckets: string) {
- // Enter in username
- await element(by.id('uid')).sendKeys(username);
-
- // Enter in full name
- await element(by.id('display_name')).click();
- await element(by.id('display_name')).sendKeys(fullname);
-
- // Enter in email
- await element(by.id('email')).click();
- await element(by.id('email')).sendKeys(email);
-
- // Enter max buckets
- await this.selectOption('max_buckets_mode', 'Custom');
- await element(by.id('max_buckets')).click();
- await element(by.id('max_buckets')).clear();
- await element(by.id('max_buckets')).sendKeys(maxbuckets);
-
- // Click the create button and wait for user to be made
- await element(by.cssContainingText('button', 'Create User')).click();
- await this.waitPresence(this.getFirstTableCellWithText(username));
- }
-
- @PageHelper.restrictTo(pages.index)
- async edit(name: string, new_fullname: string, new_email: string, new_maxbuckets: string) {
- await this.waitClickableAndClick(this.getFirstTableCellWithText(name)); // wait for table to load and click
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
-
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
-
- // Change the full name field
- await element(by.id('display_name')).click();
- await element(by.id('display_name')).clear();
- await element(by.id('display_name')).sendKeys(new_fullname);
-
- // Change the email field
- await element(by.id('email')).click();
- await element(by.id('email')).clear();
- await element(by.id('email')).sendKeys(new_email);
-
- // Change the max buckets field
- await this.selectOption('max_buckets_mode', 'Custom');
- await element(by.id('max_buckets')).click();
- await element(by.id('max_buckets')).clear();
- await element(by.id('max_buckets')).sendKeys(new_maxbuckets);
-
- const editbutton = element(by.cssContainingText('button', 'Edit User'));
- await editbutton.click();
- // Click the user and check its details table for updated content
- await this.waitClickableAndClick(this.getExpandCollapseElement(name));
- await expect($('.active.tab-pane').getText()).toMatch(new_fullname); // check full name was changed
- await expect($('.active.tab-pane').getText()).toMatch(new_email); // check email was changed
- await expect($('.active.tab-pane').getText()).toMatch(new_maxbuckets); // check max buckets was changed
- }
-
- async invalidCreate() {
- const uname = '000invalid_create_user';
- // creating this user in order to check that you can't give two users the same name
- await this.navigateTo('create');
- await this.create(uname, 'xxx', 'xxx@xxx', '1');
-
- await this.navigateTo('create');
-
- const username_field = element(by.id('uid'));
-
- // No username had been entered. Field should be invalid
- await expect(username_field.getAttribute('class')).toContain('ng-invalid');
-
- // Try to give user already taken name. Should make field invalid.
- await username_field.clear();
- await username_field.sendKeys(uname);
- await expect(username_field.getAttribute('class')).toContain('ng-invalid');
- await element(by.id('display_name')).click(); // trigger validation check
- await expect(element(by.css('#uid + .invalid-feedback')).getText()).toMatch(
- 'The chosen user ID is already in use.'
- );
-
- // check that username field is marked invalid if username has been cleared off
- await username_field.click();
- for (let i = 0; i < uname.length; i++) {
- await username_field.sendKeys(protractor.Key.BACK_SPACE);
- }
- await expect(username_field.getAttribute('class')).toContain('ng-invalid');
- await element(by.id('display_name')).click(); // trigger validation check
- await expect(element(by.css('#uid + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- // No display name has been given so field should be invalid
- await expect(element(by.id('display_name')).getAttribute('class')).toContain('ng-invalid');
-
- // display name field should also be marked invalid if given input then emptied
- await element(by.id('display_name')).click();
- await element(by.id('display_name')).sendKeys('a');
- await element(by.id('display_name')).sendKeys(protractor.Key.BACK_SPACE);
- await expect(element(by.id('display_name')).getAttribute('class')).toContain('ng-invalid');
- await username_field.click(); // trigger validation check
- await expect(element(by.css('#display_name + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- // put invalid email to make field invalid
- await element(by.id('email')).click();
- await element(by.id('email')).sendKeys('a');
- await expect(element(by.id('email')).getAttribute('class')).toContain('ng-invalid');
- await username_field.click(); // trigger validation check
- await expect(element(by.css('#email + .invalid-feedback')).getText()).toMatch(
- 'This is not a valid email address.'
- );
-
- // put negative max buckets to make field invalid
- await this.expectSelectOption('max_buckets_mode', 'Custom');
- await element(by.id('max_buckets')).click();
- await element(by.id('max_buckets')).clear();
- await element(by.id('max_buckets')).sendKeys('-5');
- await expect(element(by.id('max_buckets')).getAttribute('class')).toContain('ng-invalid');
- await username_field.click(); // trigger validation check
- await expect(element(by.css('#max_buckets + .invalid-feedback')).getText()).toMatch(
- 'The entered value must be >= 1.'
- );
-
- await this.navigateTo();
- await this.delete(uname);
- }
-
- async invalidEdit() {
- const uname = '000invalid_edit_user';
- // creating this user to edit for the test
- await this.navigateTo('create');
- await this.create(uname, 'xxx', 'xxx@xxx', '1');
-
- await this.navigateTo();
-
- // wait for table to load and click on the bucket you want to edit in the table
- await this.waitClickableAndClick(this.getFirstTableCellWithText(uname));
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
-
- await this.waitTextToBePresent(this.getBreadcrumb(), 'Edit');
-
- // put invalid email to make field invalid
- await element(by.id('email')).click();
- await element(by.id('email')).clear();
- await element(by.id('email')).sendKeys('a');
- await this.waitFn(
- async () => !(await element(by.id('email')).getAttribute('class')).includes('ng-pending')
- );
- await expect(element(by.id('email')).getAttribute('class')).toContain('ng-invalid');
- await element(by.id('display_name')).click(); // trigger validation check
- await expect(element(by.css('#email + .invalid-feedback')).getText()).toMatch(
- 'This is not a valid email address.'
- );
-
- // empty the display name field making it invalid
- await element(by.id('display_name')).click();
- for (let i = 0; i < 3; i++) {
- await element(by.id('display_name')).sendKeys(protractor.Key.BACK_SPACE);
- }
- await expect(element(by.id('display_name')).getAttribute('class')).toContain('ng-invalid');
- await element(by.id('email')).click(); // trigger validation check
- await expect(element(by.css('#display_name + .invalid-feedback')).getText()).toMatch(
- 'This field is required.'
- );
-
- // put negative max buckets to make field invalid
- await this.expectSelectOption('max_buckets_mode', 'Custom');
- await element(by.id('max_buckets')).click();
- await element(by.id('max_buckets')).clear();
- await element(by.id('max_buckets')).sendKeys('-5');
- await expect(element(by.id('max_buckets')).getAttribute('class')).toContain('ng-invalid');
- await element(by.id('email')).click(); // trigger validation check
- await expect(element(by.css('#max_buckets + .invalid-feedback')).getText()).toMatch(
- 'The entered value must be >= 1.'
- );
-
- await this.navigateTo();
- await this.delete(uname);
- }
-}
+++ /dev/null
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "outDir": "../out-tsc/e2e",
- "baseUrl": "./",
- "module": "commonjs",
- "target": "es5",
- "types": [
- "jasmine",
- "jasminewd2",
- "node"
- ],
- "noEmit": true
- }
-}
+++ /dev/null
-import { browser } from 'protractor';
-import { IscsiPageHelper } from '../block/iscsi.po';
-import { HostsPageHelper } from '../cluster/hosts.po';
-import { MonitorsPageHelper } from '../cluster/monitors.po';
-import { OSDsPageHelper } from '../cluster/osds.po';
-import { PageHelper } from '../page-helper.po';
-import { PoolPageHelper } from '../pools/pools.po';
-import { DaemonsPageHelper } from '../rgw/daemons.po';
-import { DashboardPageHelper } from './dashboard.po';
-
-describe('Dashboard Main Page', () => {
- let dashboard: DashboardPageHelper;
- let daemons: DaemonsPageHelper;
- let hosts: HostsPageHelper;
- let osds: OSDsPageHelper;
- let pools: PoolPageHelper;
- let monitors: MonitorsPageHelper;
- let iscsi: IscsiPageHelper;
-
- beforeAll(() => {
- dashboard = new DashboardPageHelper();
- daemons = new DaemonsPageHelper();
- hosts = new HostsPageHelper();
- osds = new OSDsPageHelper();
- pools = new PoolPageHelper();
- monitors = new MonitorsPageHelper();
- iscsi = new IscsiPageHelper();
- });
-
- afterEach(async () => {
- await DashboardPageHelper.checkConsole();
- });
-
- describe('Check that all hyperlinks on info cards lead to the correct page and fields exist', () => {
- beforeEach(async () => {
- await dashboard.navigateTo();
- });
-
- it('should ensure that all linked info cards lead to correct page', async () => {
- const expectationMap = {
- Monitors: 'Monitors',
- OSDs: 'OSDs',
- Hosts: 'Hosts',
- 'Object Gateways': 'Daemons',
- 'iSCSI Gateways': 'Overview',
- Pools: 'Pools'
- };
-
- for (const [linkText, breadcrumbText] of Object.entries(expectationMap)) {
- await expect(browser.getCurrentUrl()).toContain('/#/dashboard');
- await dashboard.clickInfoCardLink(linkText);
- await dashboard.waitTextToBePresent(dashboard.getBreadcrumb(), breadcrumbText);
- await dashboard.navigateBack();
- }
- });
-
- it('should verify that info cards exist on dashboard in proper order', async () => {
- // Ensures that info cards are all displayed on the dashboard tab while being in the proper
- // order, checks for card title and position via indexing into a list of all info cards.
- const order = [
- 'Cluster Status',
- 'Monitors',
- 'OSDs',
- 'Manager Daemons',
- 'Hosts',
- 'Object Gateways',
- 'Metadata Servers',
- 'iSCSI Gateways',
- 'Client IOPS',
- 'Client Throughput',
- 'Client Read/Write',
- 'Recovery Throughput',
- 'Scrub',
- 'Pools',
- 'Raw Capacity',
- 'Objects',
- 'PGs per OSD',
- 'PG Status'
- ];
-
- for (let i = 0; i < order.length; i++) {
- await expect((await dashboard.infoCard(i)).getText()).toContain(
- order[i],
- `Order of ${order[i]} seems to be wrong`
- );
- }
- });
-
- it('should verify that info card group titles are present and in the right order', async () => {
- await expect(browser.getCurrentUrl()).toContain('/#/dashboard');
- await expect(dashboard.infoGroupTitle(0)).toBe('Status');
- await expect(dashboard.infoGroupTitle(1)).toBe('Performance');
- await expect(dashboard.infoGroupTitle(2)).toBe('Capacity');
- });
- });
-
- it('Should check that dashboard cards have correct information', async () => {
- interface TestSpec {
- cardName: string;
- regexMatcher?: RegExp;
- pageObject: PageHelper;
- }
-
- const testSpecs: TestSpec[] = [
- { cardName: 'Object Gateways', regexMatcher: /(\d+)\s+total/, pageObject: daemons },
- { cardName: 'Monitors', regexMatcher: /(\d+)\s+\(quorum/, pageObject: monitors },
- { cardName: 'Hosts', regexMatcher: /(\d+)\s+total/, pageObject: hosts },
- { cardName: 'OSDs', regexMatcher: /(\d+)\s+total/, pageObject: osds },
- { cardName: 'Pools', pageObject: pools },
- { cardName: 'iSCSI Gateways', regexMatcher: /(\d+)\s+total/, pageObject: iscsi }
- ];
-
- for (let i = 0; i < testSpecs.length; i++) {
- const spec = testSpecs[i];
- await dashboard.navigateTo();
- const infoCardBodyText = await dashboard.infoCardBodyText(spec.cardName);
- let dashCount = 0;
- if (spec.regexMatcher) {
- const match = infoCardBodyText.match(new RegExp(spec.regexMatcher));
- if (match && match.length > 1) {
- dashCount = Number(match[1]);
- } else {
- return Promise.reject(
- `Regex ${spec.regexMatcher} did not find a match for card with name ` +
- `${spec.cardName}`
- );
- }
- } else {
- dashCount = Number(infoCardBodyText);
- }
- await spec.pageObject.navigateTo();
- const tableCount = await spec.pageObject.getTableTotalCount();
- await expect(dashCount).toBe(
- tableCount,
- `Text of card "${spec.cardName}" and regex "${spec.regexMatcher}" resulted in ${dashCount} ` +
- `but did not match table count ${tableCount}`
- );
- }
- });
-});
+++ /dev/null
-import { $, $$, by, ElementFinder } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class DashboardPageHelper extends PageHelper {
- pages = {
- index: '/#/dashboard'
- };
-
- async infoGroupTitle(index: number): Promise<string> {
- return $$('.info-group-title').get(index).getText();
- }
-
- async clickInfoCardLink(cardName: string): Promise<void> {
- await $(`cd-info-card[cardtitle="${cardName}"]`).element(by.linkText(cardName)).click();
- }
-
- async infoCard(indexOrTitle: number | string): Promise<ElementFinder> {
- let infoCards = $$('cd-info-card');
- if (typeof indexOrTitle === 'number') {
- if ((await infoCards.count()) <= indexOrTitle) {
- return Promise.reject(
- `No element found for index ${indexOrTitle}. Elements array has ` +
- `only ${await infoCards.count()} elements.`
- );
- }
- return infoCards.get(indexOrTitle);
- } else if (typeof indexOrTitle === 'string') {
- infoCards = infoCards.filter(
- async (e) => (await e.$('.card-title').getText()) === indexOrTitle
- );
- if ((await infoCards.count()) === 0) {
- return Promise.reject(`No element found for title "${indexOrTitle}"`);
- }
- return infoCards.first();
- }
- }
-
- async infoCardBodyText(
- infoCard: ElementFinder | Promise<ElementFinder> | string
- ): Promise<string> {
- let _infoCard: ElementFinder;
- if (typeof infoCard === 'string') {
- _infoCard = await this.infoCard(infoCard);
- } else {
- _infoCard = typeof infoCard.then === 'function' ? await infoCard : infoCard;
- }
- return _infoCard.$('.card-text').getText();
- }
-}
+++ /dev/null
-import { PoolPageHelper } from '../pools/pools.po';
-import { NotificationSidebarPageHelper } from './notification.po';
-
-describe('Notification page', () => {
- let notification: NotificationSidebarPageHelper;
- let pools: PoolPageHelper;
-
- beforeAll(() => {
- notification = new NotificationSidebarPageHelper();
- pools = new PoolPageHelper();
- });
-
- afterEach(async () => {
- await NotificationSidebarPageHelper.checkConsole();
- });
-
- it('should open notification sidebar', async () => {
- await notification.waitInvisibility(notification.getSidebar());
- await notification.open();
- await notification.waitVisibility(notification.getSidebar());
- });
-
- it('should display a running task', async () => {
- const poolName = 'e2e_notification_pool';
-
- await pools.navigateTo('create');
- await pools.create(poolName, 8);
- await pools.edit_pool_pg(poolName, 4, false);
- await notification.waitStaleness(notification.getToast());
-
- // Check that running task is shown.
- await notification.open();
- await notification.waitFn(async () => {
- const task = await notification.getTasks().first();
- const text = await task.getText();
- return text.includes(poolName);
- }, 'Timed out verifying task.');
-
- // Delete pool after task is complete (otherwise we get an error).
- await notification.waitFn(
- async () => {
- const tasks = await notification.getTasks();
- return tasks.length === 0 ? true : !(await tasks[0].getText()).includes(poolName);
- },
- 'Timed out waiting for task to complete.',
- 40000
- );
- await pools.delete(poolName);
- });
-
- it('should have notifications', async () => {
- await notification.open();
- await expect((await notification.getNotifications()).length).toBeGreaterThan(0);
- });
-
- it('should clear notifications', async () => {
- await notification.waitStaleness(notification.getToast());
- await expect((await notification.getNotifications()).length).toBeGreaterThan(0);
- await notification.waitVisibility(notification.getClearNotficationsBtn());
-
- // It can happen that although notifications are cleared, by the time we check the
- // notifications amount, another notification can appear, so we check it more than once (if needed).
- await notification.waitClickableAndClick(notification.getClearNotficationsBtn());
- await notification.waitFn(async () => {
- const notifications = await notification.getNotifications();
- if (notifications.length > 0) {
- await notification.waitClickableAndClick(notification.getClearNotficationsBtn());
- return false;
- }
- return true;
- }, 'Timed out checking that notifications are cleared.');
- });
-});
+++ /dev/null
-import { by, element } from 'protractor';
-
-import { PageHelper } from '../page-helper.po';
-
-export class NotificationSidebarPageHelper extends PageHelper {
- getNotificatinoIcon() {
- return element(by.css('cd-notifications a'));
- }
-
- getSidebar() {
- return element(by.css('cd-notifications-sidebar'));
- }
-
- getTasks() {
- return this.getSidebar().all(by.css('.card.tc_task'));
- }
-
- getNotifications() {
- return this.getSidebar().all(by.css('.card.tc_notification'));
- }
-
- getClearNotficationsBtn() {
- return this.getSidebar().element(by.css('button.btn-block'));
- }
-
- getCloseBtn() {
- return this.getSidebar().element(by.css('button.close'));
- }
-
- async open() {
- await this.waitClickableAndClick(this.getNotificatinoIcon());
- return this.waitVisibility(this.getSidebar());
- }
-}
+++ /dev/null
-import { RoleMgmtPageHelper } from './role-mgmt.po';
-
-describe('Role Management page', () => {
- let roleMgmt: RoleMgmtPageHelper;
- const role_name = 'user_mgmt_create_edit_delete_role';
-
- beforeAll(() => {
- roleMgmt = new RoleMgmtPageHelper();
- });
-
- afterEach(async () => {
- await RoleMgmtPageHelper.checkConsole();
- });
-
- describe('breadcrumb tests', () => {
- it('should check breadcrumb on roles tab on user management page', async () => {
- await roleMgmt.navigateTo();
- await roleMgmt.waitTextToBePresent(roleMgmt.getBreadcrumb(), 'Roles');
- });
-
- it('should check breadcrumb on role creation page', async () => {
- await roleMgmt.navigateTo('create');
- await roleMgmt.waitTextToBePresent(roleMgmt.getBreadcrumb(), 'Create');
- });
- });
-
- describe('role create, edit & delete test', () => {
- it('should create a role', async () => {
- await roleMgmt.create(role_name, 'An interesting description');
- });
-
- it('should edit a role', async () => {
- await roleMgmt.edit(role_name, 'A far more interesting description');
- });
-
- it('should delete a role', async () => {
- await roleMgmt.navigateTo();
- await roleMgmt.delete(role_name);
- });
- });
-});
+++ /dev/null
-import { by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class RoleMgmtPageHelper extends PageHelper {
- pages = {
- index: '/#/user-management/roles',
- create: '/#/user-management/roles/create'
- };
-
- async create(name: string, description: string): Promise<void> {
- await this.navigateTo('create');
-
- // fill in fields
- await element(by.id('name')).sendKeys(name);
- await element(by.id('description')).sendKeys(description);
-
- // Click the create button and wait for role to be made
- const createButton = element(by.cssContainingText('button', 'Create Role'));
- await createButton.click();
-
- await this.waitPresence(this.getFirstTableCellWithText(name));
- }
-
- async edit(name: string, description: string): Promise<void> {
- await this.navigateTo();
-
- await this.getFirstTableCellWithText(name).click(); // select role from table
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
-
- // fill in fields with new values
- await element(by.id('description')).clear();
- await element(by.id('description')).sendKeys(description);
-
- // Click the edit button and check new values are present in table
- const editButton = element(by.cssContainingText('button', 'Edit Role'));
- await editButton.click();
-
- await this.waitPresence(this.getFirstTableCellWithText(name));
- await this.waitPresence(this.getFirstTableCellWithText(description));
- }
-}
+++ /dev/null
-import { UserMgmtPageHelper } from './user-mgmt.po';
-
-describe('User Management page', () => {
- let userMgmt: UserMgmtPageHelper;
- const user_name = 'user_mgmt_create_edit_delete_user';
-
- beforeAll(() => {
- userMgmt = new UserMgmtPageHelper();
- });
-
- afterEach(async () => {
- await UserMgmtPageHelper.checkConsole();
- });
-
- describe('breadcrumb tests', () => {
- it('should check breadcrumb on users tab of user management page', async () => {
- await userMgmt.navigateTo();
- await userMgmt.waitTextToBePresent(userMgmt.getBreadcrumb(), 'Users');
- });
-
- it('should check breadcrumb on user creation page', async () => {
- await userMgmt.navigateTo('create');
- await userMgmt.waitTextToBePresent(userMgmt.getBreadcrumb(), 'Create');
- });
- });
-
- describe('user create, edit & delete test', () => {
- it('should create a user', async () => {
- await userMgmt.create(user_name, 'cool_password', 'Jeff', 'realemail@realwebsite.com');
- });
-
- it('should edit a user', async () => {
- await userMgmt.edit(user_name, 'cool_password_number_2', 'Geoff', 'w@m');
- });
-
- it('should delete a user', async () => {
- await userMgmt.navigateTo();
- await userMgmt.delete(user_name);
- });
- });
-});
+++ /dev/null
-import { by, element } from 'protractor';
-import { PageHelper } from '../page-helper.po';
-
-export class UserMgmtPageHelper extends PageHelper {
- pages = {
- index: '/#/user-management/users',
- create: '/#/user-management/users/create'
- };
-
- async create(username: string, password: string, name: string, email: string): Promise<void> {
- await this.navigateTo('create');
-
- // fill in fields
- await element(by.id('username')).sendKeys(username);
- await element(by.id('password')).sendKeys(password);
- await element(by.id('confirmpassword')).sendKeys(password);
- await element(by.id('name')).sendKeys(name);
- await element(by.id('email')).sendKeys(email);
-
- // Click the create button and wait for user to be made
- const createButton = element(by.cssContainingText('button', 'Create User'));
- await createButton.click();
- await this.waitPresence(this.getFirstTableCellWithText(username));
- }
-
- async edit(username: string, password: string, name: string, email: string): Promise<void> {
- await this.navigateTo();
-
- await this.getFirstTableCellWithText(username).click(); // select user from table
- await element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page
-
- // fill in fields with new values
- await element(by.id('password')).clear();
- await element(by.id('password')).sendKeys(password);
- await element(by.id('confirmpassword')).clear();
- await element(by.id('confirmpassword')).sendKeys(password);
- await element(by.id('name')).clear();
- await element(by.id('name')).sendKeys(name);
- await element(by.id('email')).clear();
- await element(by.id('email')).sendKeys(email);
-
- // Click the edit button and check new values are present in table
- const editButton = element(by.cssContainingText('button', 'Edit User'));
- await editButton.click();
- await this.waitPresence(this.getFirstTableCellWithText(email));
- await this.waitPresence(this.getFirstTableCellWithText(name));
- }
-}
"worker-plugin": "3.2.0"
},
"dependencies": {
+ "core-js": {
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
+ "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
+ "dev": true
+ },
"glob": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
"convert-source-map": "^1.5.1",
"dependency-graph": "^0.7.2",
"magic-string": "^0.25.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"reflect-metadata": "^0.1.2",
"source-map": "^0.6.1",
"tslib": "^1.9.0",
}
},
"minimist": {
+ "version": "1.2.5",
+ "bundled": true,
"dev": true,
- "optional": true,
- "version": "1.2.5"
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"dev": true,
"optional": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"ms": {
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
"readable-stream": {
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
- "minimist": {
- "version": "1.2.5"
- },
"os-locale": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
},
"dependencies": {
"json5": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
- "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"semver": {
}
},
"@babel/generator": {
- "version": "7.9.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz",
- "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz",
+ "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==",
"dev": true,
"requires": {
- "@babel/types": "^7.9.0",
+ "@babel/types": "^7.9.5",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
}
},
"@babel/helper-function-name": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz",
- "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+ "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.8.3",
"@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/types": "^7.9.5"
}
},
"@babel/helper-get-function-arity": {
}
},
"@babel/helper-validator-identifier": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz",
- "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+ "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
"dev": true
},
"@babel/helper-wrap-function": {
}
},
"@babel/plugin-proposal-object-rest-spread": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz",
- "integrity": "sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.5.tgz",
+ "integrity": "sha512-VP2oXvAf7KCYTthbUHwBlewbl1Iq059f6seJGsxMizaCdgHIeczOr7FBqELhSqfkIl04Fi8okzWzl63UKbQmmg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.0"
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-transform-parameters": "^7.9.5"
}
},
"@babel/plugin-proposal-optional-catch-binding": {
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz",
+ "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-syntax-dynamic-import": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz",
+ "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-syntax-nullish-coalescing-operator": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
"@babel/helper-plugin-utils": "^7.8.0"
}
},
+ "@babel/plugin-syntax-numeric-separator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz",
+ "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
"@babel/plugin-syntax-object-rest-spread": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
}
},
"@babel/plugin-transform-classes": {
- "version": "7.9.2",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz",
- "integrity": "sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz",
+ "integrity": "sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/helper-define-map": "^7.8.3",
- "@babel/helper-function-name": "^7.8.3",
+ "@babel/helper-function-name": "^7.9.5",
"@babel/helper-optimise-call-expression": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/helper-replace-supers": "^7.8.6",
}
},
"@babel/plugin-transform-destructuring": {
- "version": "7.8.8",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz",
- "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz",
+ "integrity": "sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3"
}
},
"@babel/plugin-transform-parameters": {
- "version": "7.9.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz",
- "integrity": "sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz",
+ "integrity": "sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.8.3",
}
},
"@babel/traverse": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.0.tgz",
- "integrity": "sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz",
+ "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.9.0",
- "@babel/helper-function-name": "^7.8.3",
+ "@babel/generator": "^7.9.5",
+ "@babel/helper-function-name": "^7.9.5",
"@babel/helper-split-export-declaration": "^7.8.3",
"@babel/parser": "^7.9.0",
- "@babel/types": "^7.9.0",
+ "@babel/types": "^7.9.5",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.9.0",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz",
- "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==",
+ "version": "7.9.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
+ "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
"dev": true,
"requires": {
- "@babel/helper-validator-identifier": "^7.9.0",
+ "@babel/helper-validator-identifier": "^7.9.5",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
"dev": true,
"requires": {
"exec-sh": "^0.3.2",
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.0"
}
},
"@compodoc/compodoc": {
"loglevel-plugin-prefix": "^0.8.4",
"lunr": "^2.3.6",
"marked": "^0.7.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"opencollective-postinstall": "^2.0.2",
"os-name": "^3.1.0",
"pdfmake": "^0.1.60",
}
},
"json5": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
- "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
- },
- "minimist": {
- "version": "1.2.5"
}
}
},
}
}
},
+ "@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=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "cli-cursor": "^1.0.2",
+ "date-fns": "^1.27.2",
+ "figures": "^1.7.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "cli-cursor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^1.0.1"
+ }
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "onetime": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+ "dev": true,
+ "requires": {
+ "exit-hook": "^1.0.0",
+ "onetime": "^1.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "@cypress/request": {
+ "version": "2.88.5",
+ "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz",
+ "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "@cypress/xvfb": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
+ "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.1.0",
+ "lodash.once": "^4.1.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
"@dsherret/to-absolute-glob": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
"is-negated-glob": "^1.0.0"
}
},
+ "@hapi/address": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz",
+ "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==",
+ "dev": true
+ },
+ "@hapi/formula": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-1.2.0.tgz",
+ "integrity": "sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==",
+ "dev": true
+ },
+ "@hapi/hoek": {
+ "version": "8.5.1",
+ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz",
+ "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==",
+ "dev": true
+ },
+ "@hapi/joi": {
+ "version": "16.1.8",
+ "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-16.1.8.tgz",
+ "integrity": "sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==",
+ "dev": true,
+ "requires": {
+ "@hapi/address": "^2.1.2",
+ "@hapi/formula": "^1.2.0",
+ "@hapi/hoek": "^8.2.4",
+ "@hapi/pinpoint": "^1.0.2",
+ "@hapi/topo": "^3.1.3"
+ }
+ },
+ "@hapi/pinpoint": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-1.0.2.tgz",
+ "integrity": "sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==",
+ "dev": true
+ },
+ "@hapi/topo": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz",
+ "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==",
+ "dev": true,
+ "requires": {
+ "@hapi/hoek": "^8.3.0"
+ }
+ },
"@istanbuljs/load-nyc-config": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz",
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
"dev": true
},
"@jest/console": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.2.3.tgz",
- "integrity": "sha512-k+37B1aSvOt9tKHWbZZSOy1jdgzesB0bj96igCVUG1nAH1W5EoUfgc5EXbBVU08KSLvkVdWopLXaO3xfVGlxtQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.4.0.tgz",
+ "integrity": "sha512-CfE0erx4hdJ6t7RzAcE1wLG6ZzsHSmybvIBQDoCkDM1QaSeWL9wJMzID/2BbHHa7ll9SsbbK43HjbERbBaFX2A==",
"dev": true,
"requires": {
- "@jest/source-map": "^25.2.1",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
- "jest-util": "^25.2.3",
+ "jest-message-util": "^25.4.0",
+ "jest-util": "^25.4.0",
"slash": "^3.0.0"
},
"dependencies": {
}
},
"@jest/core": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.2.4.tgz",
- "integrity": "sha512-WcWYShl0Bqfcb32oXtjwbiR78D/djhMdJW+ulp4/bmHgeODcsieqUJfUH+kEv8M7VNV77E6jds5aA+WuGh1nmg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.4.0.tgz",
+ "integrity": "sha512-h1x9WSVV0+TKVtATGjyQIMJENs8aF6eUjnCoi4jyRemYZmekLr8EJOGQqTWEX8W6SbZ6Skesy9pGXrKeAolUJw==",
"dev": true,
"requires": {
- "@jest/console": "^25.2.3",
- "@jest/reporters": "^25.2.4",
- "@jest/test-result": "^25.2.4",
- "@jest/transform": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/console": "^25.4.0",
+ "@jest/reporters": "^25.4.0",
+ "@jest/test-result": "^25.4.0",
+ "@jest/transform": "^25.4.0",
+ "@jest/types": "^25.4.0",
"ansi-escapes": "^4.2.1",
"chalk": "^3.0.0",
"exit": "^0.1.2",
"graceful-fs": "^4.2.3",
- "jest-changed-files": "^25.2.3",
- "jest-config": "^25.2.4",
- "jest-haste-map": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-regex-util": "^25.2.1",
- "jest-resolve": "^25.2.3",
- "jest-resolve-dependencies": "^25.2.4",
- "jest-runner": "^25.2.4",
- "jest-runtime": "^25.2.4",
- "jest-snapshot": "^25.2.4",
- "jest-util": "^25.2.3",
- "jest-validate": "^25.2.3",
- "jest-watcher": "^25.2.4",
+ "jest-changed-files": "^25.4.0",
+ "jest-config": "^25.4.0",
+ "jest-haste-map": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-regex-util": "^25.2.6",
+ "jest-resolve": "^25.4.0",
+ "jest-resolve-dependencies": "^25.4.0",
+ "jest-runner": "^25.4.0",
+ "jest-runtime": "^25.4.0",
+ "jest-snapshot": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-validate": "^25.4.0",
+ "jest-watcher": "^25.4.0",
"micromatch": "^4.0.2",
"p-each-series": "^2.1.0",
"realpath-native": "^2.0.0",
}
},
"@jest/environment": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.2.4.tgz",
- "integrity": "sha512-wA4xlhD19/gukkDpJ5HQsTle0pgnzI5qMFEjw267lpTDC8d9N7Ihqr5pI+l0p8Qn1SQhai+glSqxrGdzKy4jxw==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.4.0.tgz",
+ "integrity": "sha512-KDctiak4mu7b4J6BIoN/+LUL3pscBzoUCP+EtSPd2tK9fqyDY5OF+CmkBywkFWezS9tyH5ACOQNtpjtueEDH6Q==",
"dev": true,
"requires": {
- "@jest/fake-timers": "^25.2.4",
- "@jest/types": "^25.2.3",
- "jest-mock": "^25.2.3"
+ "@jest/fake-timers": "^25.4.0",
+ "@jest/types": "^25.4.0",
+ "jest-mock": "^25.4.0"
}
},
"@jest/fake-timers": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.2.4.tgz",
- "integrity": "sha512-oC1TJiwfMcBttVN7Wz+VZnqEAgYTiEMu0QLOXpypR89nab0uCB31zm/QeBZddhSstn20qe3yqOXygp6OwvKT/Q==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.4.0.tgz",
+ "integrity": "sha512-lI9z+VOmVX4dPPFzyj0vm+UtaB8dCJJ852lcDnY0uCPRvZAaVGnMwBBc1wxtf+h7Vz6KszoOvKAt4QijDnHDkg==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-mock": "^25.2.3",
- "jest-util": "^25.2.3",
+ "@jest/types": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-mock": "^25.4.0",
+ "jest-util": "^25.4.0",
"lolex": "^5.0.0"
}
},
"@jest/reporters": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.2.4.tgz",
- "integrity": "sha512-VHbLxM03jCc+bTLOluW/IqHR2G0Cl0iATwIQbuZtIUast8IXO4fD0oy4jpVGpG5b20S6REA8U3BaQoCW/CeVNQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.4.0.tgz",
+ "integrity": "sha512-bhx/buYbZgLZm4JWLcRJ/q9Gvmd3oUh7k2V7gA4ZYBx6J28pIuykIouclRdiAC6eGVX1uRZT+GK4CQJLd/PwPg==",
"dev": true,
"requires": {
"@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^25.2.3",
- "@jest/test-result": "^25.2.4",
- "@jest/transform": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/console": "^25.4.0",
+ "@jest/test-result": "^25.4.0",
+ "@jest/transform": "^25.4.0",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
"collect-v8-coverage": "^1.0.0",
"exit": "^0.1.2",
"istanbul-lib-instrument": "^4.0.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.0.0",
- "jest-haste-map": "^25.2.3",
- "jest-resolve": "^25.2.3",
- "jest-util": "^25.2.3",
- "jest-worker": "^25.2.1",
+ "istanbul-reports": "^3.0.2",
+ "jest-haste-map": "^25.4.0",
+ "jest-resolve": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-worker": "^25.4.0",
"node-notifier": "^6.0.0",
"slash": "^3.0.0",
"source-map": "^0.6.0",
"string-length": "^3.1.0",
"terminal-link": "^2.0.0",
- "v8-to-istanbul": "^4.0.1"
+ "v8-to-istanbul": "^4.1.3"
},
"dependencies": {
"ansi-styles": {
"dev": true
},
"jest-worker": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz",
- "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz",
+ "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
}
},
"@jest/source-map": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.1.tgz",
- "integrity": "sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A==",
+ "version": "25.2.6",
+ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.6.tgz",
+ "integrity": "sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ==",
"dev": true,
"requires": {
"callsites": "^3.0.0",
}
},
"@jest/test-result": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.2.4.tgz",
- "integrity": "sha512-AI7eUy+q2lVhFnaibDFg68NGkrxVWZdD6KBr9Hm6EvN0oAe7GxpEwEavgPfNHQjU2mi6g+NsFn/6QPgTUwM1qg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.4.0.tgz",
+ "integrity": "sha512-8BAKPaMCHlL941eyfqhWbmp3MebtzywlxzV+qtngQ3FH+RBqnoSAhNEPj4MG7d2NVUrMOVfrwuzGpVIK+QnMAA==",
"dev": true,
"requires": {
- "@jest/console": "^25.2.3",
- "@jest/transform": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/console": "^25.4.0",
+ "@jest/types": "^25.4.0",
"@types/istanbul-lib-coverage": "^2.0.0",
"collect-v8-coverage": "^1.0.0"
}
},
"@jest/test-sequencer": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.2.4.tgz",
- "integrity": "sha512-TEZm/Rkd6YgskdpTJdYLBtu6Gc11tfWPuSpatq0duH77ekjU8dpqX2zkPdY/ayuHxztV5LTJoV5BLtI9mZfXew==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.4.0.tgz",
+ "integrity": "sha512-240cI+nsM3attx2bMp9uGjjHrwrpvxxrZi8Tyqp/cfOzl98oZXVakXBgxODGyBYAy/UGXPKXLvNc2GaqItrsJg==",
"dev": true,
"requires": {
- "@jest/test-result": "^25.2.4",
- "jest-haste-map": "^25.2.3",
- "jest-runner": "^25.2.4",
- "jest-runtime": "^25.2.4"
+ "@jest/test-result": "^25.4.0",
+ "jest-haste-map": "^25.4.0",
+ "jest-runner": "^25.4.0",
+ "jest-runtime": "^25.4.0"
}
},
"@jest/transform": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.2.4.tgz",
- "integrity": "sha512-6eRigvb+G6bs4kW5j1/y8wu4nCrmVuIe0epPBbiWaYlwawJ8yi1EIyK3d/btDqmBpN5GpN4YhR6iPPnDmkYdTA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.4.0.tgz",
+ "integrity": "sha512-t1w2S6V1sk++1HHsxboWxPEuSpN8pxEvNrZN+Ud/knkROWtf8LeUmz73A4ezE8476a5AM00IZr9a8FO9x1+j3g==",
"dev": true,
"requires": {
"@babel/core": "^7.1.0",
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"babel-plugin-istanbul": "^6.0.0",
"chalk": "^3.0.0",
"convert-source-map": "^1.4.0",
"fast-json-stable-stringify": "^2.0.0",
"graceful-fs": "^4.2.3",
- "jest-haste-map": "^25.2.3",
- "jest-regex-util": "^25.2.1",
- "jest-util": "^25.2.3",
+ "jest-haste-map": "^25.4.0",
+ "jest-regex-util": "^25.2.6",
+ "jest-util": "^25.4.0",
"micromatch": "^4.0.2",
"pirates": "^4.0.1",
"realpath-native": "^2.0.0",
}
},
"@jest/types": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.2.3.tgz",
- "integrity": "sha512-6oLQwO9mKif3Uph3RX5J1i3S7X7xtDHWBaaaoeKw8hOzV6YUd0qDcYcHZ6QXMHDIzSr7zzrEa51o2Ovlj6AtKQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.4.0.tgz",
+ "integrity": "sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==",
"dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
"dev": true
},
+ "@samverschueren/stream-to-observable": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz",
+ "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==",
+ "dev": true,
+ "requires": {
+ "any-observable": "^0.3.0"
+ }
+ },
"@schematics/angular": {
"version": "8.3.26",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-8.3.26.tgz",
}
},
"@sinonjs/commons": {
- "version": "1.7.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz",
- "integrity": "sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ==",
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.2.tgz",
+ "integrity": "sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
}
},
"@types/babel__core": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.6.tgz",
- "integrity": "sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg==",
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz",
+ "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==",
"dev": true,
"requires": {
"@babel/parser": "^7.1.0",
}
},
"@types/babel__traverse": {
- "version": "7.0.9",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz",
- "integrity": "sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw==",
+ "version": "7.0.10",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.10.tgz",
+ "integrity": "sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw==",
"dev": true,
"requires": {
"@babel/types": "^7.3.0"
}
},
+ "@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==",
+ "dev": true
+ },
+ "@types/bluebird": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.29.tgz",
+ "integrity": "sha512-kmVtnxTuUuhCET669irqQmPAez4KFnFVKvpleVRyfC3g+SHD1hIkFZcWLim9BVcwUBLO59o8VZE4yGCmTif8Yw==",
+ "dev": true
+ },
+ "@types/chai": {
+ "version": "4.2.7",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz",
+ "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==",
+ "dev": true
+ },
+ "@types/chai-jquery": {
+ "version": "1.1.40",
+ "resolved": "https://registry.npmjs.org/@types/chai-jquery/-/chai-jquery-1.1.40.tgz",
+ "integrity": "sha512-mCNEZ3GKP7T7kftKeIs7QmfZZQM7hslGSpYzKbOlR2a2HCFf9ph4nlMRA9UnuOETeOQYJVhJQK7MwGqNZVyUtQ==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*",
+ "@types/jquery": "*"
+ }
+ },
"@types/chart.js": {
- "version": "2.9.18",
- "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.18.tgz",
- "integrity": "sha512-D7oaYQqYGdfoa1Wv9doxQJ9Sv/W7jfbiXMT/wVRiM0AsPJsHWLRn7U46xhDkPRGmLCpQGlN2oZYBIwEpMMleog==",
+ "version": "2.9.19",
+ "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.9.19.tgz",
+ "integrity": "sha512-sFxlMb+ElfJelXh0Z8spmiLRrnXCd7CaT6WGQtckhjETK1H5i1nYKN4TOExhqPeDZ6u+w4bJ20UYqELWOEfAKQ==",
"requires": {
"moment": "^2.10.2"
}
"@types/istanbul-lib-report": "*"
}
},
- "@types/jasmine": {
- "version": "3.5.10",
- "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.10.tgz",
- "integrity": "sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew==",
- "dev": true
- },
- "@types/jasminewd2": {
- "version": "2.0.8",
- "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz",
- "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==",
- "dev": true,
- "requires": {
- "@types/jasmine": "*"
- }
- },
"@types/jest": {
"version": "25.1.4",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.1.4.tgz",
"pretty-format": "^25.1.0"
}
},
+ "@types/jquery": {
+ "version": "3.3.31",
+ "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.31.tgz",
+ "integrity": "sha512-Lz4BAJihoFw5nRzKvg4nawXPzutkv7wmfQ5121avptaSIXlDNJCUuxZxX/G+9EVidZGuO0UBlk+YjKbwRKJigg==",
+ "dev": true,
+ "requires": {
+ "@types/sizzle": "*"
+ }
+ },
"@types/lodash": {
"version": "4.14.149",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
"dev": true
},
+ "@types/mocha": {
+ "version": "5.2.7",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
+ "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
+ "dev": true
+ },
"@types/node": {
"version": "12.12.34",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz",
"integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==",
"dev": true
},
+ "@types/normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
+ "dev": true
+ },
"@types/prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==",
"dev": true
},
- "@types/q": {
- "version": "0.0.32",
- "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
- "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
- "dev": true
- },
- "@types/selenium-webdriver": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz",
- "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==",
- "dev": true
- },
"@types/simplebar": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/simplebar/-/simplebar-5.1.1.tgz",
"integrity": "sha512-nC9iBQ4dfvvzJ3iAbL1qCfwjUyaF8EO56l/ApcRXUFK2zLOb8GDXC55V08JZvpzkUxGHtWVunp17KKH/3/KFJA==",
"dev": true
},
+ "@types/sinon": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.1.tgz",
+ "integrity": "sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==",
+ "dev": true
+ },
+ "@types/sinon-chai": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.3.tgz",
+ "integrity": "sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "*",
+ "@types/sinon": "*"
+ }
+ },
+ "@types/sizzle": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
+ "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
+ "dev": true
+ },
"@types/source-list-map": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
"integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==",
"dev": true
},
- "adm-zip": {
- "version": "0.4.14",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
- "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==",
- "dev": true
- },
"agent-base": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
"dev": true
},
+ "any-observable": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz",
+ "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==",
+ "dev": true
+ },
"anymatch": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true
},
+ "arch": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.1.1.tgz",
+ "integrity": "sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg==",
+ "dev": true
+ },
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
}
},
"babel-jest": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.2.4.tgz",
- "integrity": "sha512-+yDzlyJVWrqih9i2Cvjpt7COaN8vUwCsKGtxJLzg6I0xhxD54K8mvDUCliPKLufyzHh/c5C4MRj4Vk7VMjOjIg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.4.0.tgz",
+ "integrity": "sha512-p+epx4K0ypmHuCnd8BapfyOwWwosNCYhedetQey1awddtfmEX0MmdxctGl956uwUmjwXR5VSS5xJcGX9DvdIog==",
"dev": true,
"requires": {
- "@jest/transform": "^25.2.4",
- "@jest/types": "^25.2.3",
- "@types/babel__core": "^7.1.0",
+ "@jest/transform": "^25.4.0",
+ "@jest/types": "^25.4.0",
+ "@types/babel__core": "^7.1.7",
"babel-plugin-istanbul": "^6.0.0",
- "babel-preset-jest": "^25.2.1",
+ "babel-preset-jest": "^25.4.0",
"chalk": "^3.0.0",
"slash": "^3.0.0"
},
}
},
"babel-plugin-dynamic-import-node": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz",
- "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
"dev": true,
"requires": {
"object.assign": "^4.1.0"
}
},
"babel-plugin-jest-hoist": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.1.tgz",
- "integrity": "sha512-HysbCQfJhxLlyxDbKcB2ucGYV0LjqK4h6dBoI3RtFuOxTiTWK6XGZMsHb0tGh8iJdV4hC6Z2GCHzVvDeh9i0lQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.4.0.tgz",
+ "integrity": "sha512-M3a10JCtTyKevb0MjuH6tU+cP/NVQZ82QPADqI1RQYY1OphztsCeIeQmTsHmF/NS6m0E51Zl4QNsI3odXSQF5w==",
"dev": true,
"requires": {
"@types/babel__traverse": "^7.0.6"
}
},
+ "babel-preset-current-node-syntax": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz",
+ "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==",
+ "dev": true,
+ "requires": {
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-bigint": "^7.8.3",
+ "@babel/plugin-syntax-class-properties": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ }
+ },
"babel-preset-jest": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.2.1.tgz",
- "integrity": "sha512-zXHJBM5iR8oEO4cvdF83AQqqJf3tJrXy3x8nfu2Nlqvn4cneg4Ca8M7cQvC5S9BzDDy1O0tZ9iXru9J6E3ym+A==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.4.0.tgz",
+ "integrity": "sha512-PwFiEWflHdu3JCeTr0Pb9NcHHE34qWFnPQRVPvqQITx4CsDCzs6o05923I10XvLvn9nNsRHuiVgB72wG/90ZHQ==",
"dev": true,
"requires": {
- "@babel/plugin-syntax-bigint": "^7.0.0",
- "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
- "babel-plugin-jest-hoist": "^25.2.1"
+ "babel-plugin-jest-hoist": "^25.4.0",
+ "babel-preset-current-node-syntax": "^0.1.2"
}
},
"babel-runtime": {
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
"dev": true
},
- "blocking-proxy": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
- "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
- "dev": true,
- "requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
- }
- },
"bluebird": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
"pkg-up": "^3.1.0"
}
},
- "browserstack": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.3.tgz",
- "integrity": "sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg==",
- "dev": true,
- "requires": {
- "https-proxy-agent": "^2.2.1"
- }
- },
"bs-logger": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"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=",
+ "dev": true
+ },
"buffer-equal": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz",
"unset-value": "^1.0.0"
}
},
+ "cachedir": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz",
+ "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==",
+ "dev": true
+ },
"call-me-maybe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
"color-name": "^1.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=",
+ "dev": true
+ },
"cheerio": {
"version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
}
},
"chokidar": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
- "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
+ "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.3.0"
+ "readdirp": "~3.4.0"
},
"dependencies": {
"fsevents": {
"integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==",
"dev": true
},
- "circular-json": {
- "version": "0.5.9",
- "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
- "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
- "dev": true
- },
"cjson": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cjson/-/cjson-0.5.0.tgz",
"restore-cursor": "^3.1.0"
}
},
+ "cli-table3": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
+ "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.1.2",
+ "object-assign": "^4.1.0",
+ "string-width": "^2.1.1"
+ }
+ },
+ "cli-truncate": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz",
+ "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=",
+ "dev": true,
+ "requires": {
+ "slice-ansi": "0.0.4",
+ "string-width": "^1.0.1"
+ },
+ "dependencies": {
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ }
+ }
+ },
"cli-width": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
- "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
+ "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
"dev": true
},
"cliui": {
}
},
"collect-v8-coverage": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz",
- "integrity": "sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+ "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
"dev": true
},
"collection-visit": {
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
+ "common-tags": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
+ "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==",
+ "dev": true
+ },
"commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"core-js": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
- "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
+ "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
},
"core-js-compat": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz",
- "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
+ "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
"dev": true,
"requires": {
- "browserslist": "^4.8.3",
+ "browserslist": "^4.8.5",
"semver": "7.0.0"
},
"dependencies": {
},
"dependencies": {
"ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "version": "6.12.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+ "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"dev": true
},
"schema-utils": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
- "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "version": "2.6.6",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
+ "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
"dev": true,
"requires": {
"ajv": "^6.12.0",
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+ "cypress": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/cypress/-/cypress-4.4.0.tgz",
+ "integrity": "sha512-ZpsV3pVemANGi4Cxu0UIqFv23uHdDJZYlKY+8P/eixujCpI1TQ5RSPBp2grfV3ZvlGYrOXPJY44j9iEh1xoQug==",
+ "dev": true,
+ "requires": {
+ "@cypress/listr-verbose-renderer": "0.4.1",
+ "@cypress/request": "2.88.5",
+ "@cypress/xvfb": "1.2.4",
+ "@types/blob-util": "1.3.3",
+ "@types/bluebird": "3.5.29",
+ "@types/chai": "4.2.7",
+ "@types/chai-jquery": "1.1.40",
+ "@types/jquery": "3.3.31",
+ "@types/lodash": "4.14.149",
+ "@types/minimatch": "3.0.3",
+ "@types/mocha": "5.2.7",
+ "@types/sinon": "7.5.1",
+ "@types/sinon-chai": "3.2.3",
+ "@types/sizzle": "2.3.2",
+ "arch": "2.1.1",
+ "bluebird": "3.7.2",
+ "cachedir": "2.3.0",
+ "chalk": "2.4.2",
+ "check-more-types": "2.24.0",
+ "cli-table3": "0.5.1",
+ "commander": "4.1.0",
+ "common-tags": "1.8.0",
+ "debug": "4.1.1",
+ "eventemitter2": "4.1.2",
+ "execa": "1.0.0",
+ "executable": "4.1.1",
+ "extract-zip": "1.7.0",
+ "fs-extra": "8.1.0",
+ "getos": "3.1.4",
+ "is-ci": "2.0.0",
+ "is-installed-globally": "0.1.0",
+ "lazy-ass": "1.6.0",
+ "listr": "0.14.3",
+ "lodash": "4.17.15",
+ "log-symbols": "3.0.0",
+ "minimist": "1.2.5",
+ "moment": "2.24.0",
+ "ospath": "1.2.2",
+ "pretty-bytes": "5.3.0",
+ "ramda": "0.26.1",
+ "request-progress": "3.0.0",
+ "supports-color": "7.1.0",
+ "tmp": "0.1.0",
+ "untildify": "4.0.0",
+ "url": "0.11.0",
+ "yauzl": "2.10.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz",
+ "integrity": "sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==",
+ "dev": true
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "dev": true,
+ "requires": {
+ "rimraf": "^2.6.3"
+ }
+ }
+ }
+ },
"d": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
"whatwg-url": "^7.0.0"
}
},
+ "date-fns": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
+ "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==",
+ "dev": true
+ },
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"dev": true
},
"diff-sequences": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.1.tgz",
- "integrity": "sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg==",
+ "version": "25.2.6",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz",
+ "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==",
"dev": true
},
"diffie-hellman": {
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.391",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.391.tgz",
- "integrity": "sha512-WOi6loSnDmfICOqGRrgeK7bZeWDAbGjCptDhI5eyJAqSzWfoeRuOOU1rOTZRL29/9AaxTndZB6Uh8YrxRfZJqw==",
+ "version": "1.3.418",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.418.tgz",
+ "integrity": "sha512-i2QrQtHes5fK/F9QGG5XacM5WKEuR322fxTYF9e8O9Gu0mc0WmjjwGpV8c7Htso6Zf2Di18lc3SIPxmMeRFBug==",
+ "dev": true
+ },
+ "elegant-spinner": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz",
+ "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=",
"dev": true
},
"elliptic": {
"dev": true
},
"escodegen": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz",
- "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
+ "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
"dev": true,
"requires": {
- "esprima": "^3.1.3",
+ "esprima": "^4.0.1",
"estraverse": "^4.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1",
"source-map": "~0.6.1"
},
"dependencies": {
- "esprima": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
- "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
- "dev": true
- },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"through": "~2.3.1"
}
},
+ "eventemitter2": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-4.1.2.tgz",
+ "integrity": "sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU=",
+ "dev": true
+ },
"eventemitter3": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"strip-eof": "^1.0.0"
}
},
+ "executable": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
+ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
+ "dev": true,
+ "requires": {
+ "pify": "^2.2.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
"exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
"dev": true
},
+ "exit-hook": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+ "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
+ "dev": true
+ },
"expand-brackets": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
}
},
"expect": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/expect/-/expect-25.2.4.tgz",
- "integrity": "sha512-hfuPhPds4yOsZtIw4kwAg70r0hqGmpqekgA+VX7pf/3wZ6FY+xIOXZhNsPMMMsspYG/YIsbAiwqsdnD4Ht+bCA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-25.4.0.tgz",
+ "integrity": "sha512-7BDIX99BTi12/sNGJXA9KMRcby4iAmu1xccBOhyKCyEhjcVKS3hPmHdA/4nSI9QGIOkUropKqr3vv7WMDM5lvQ==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"ansi-styles": "^4.0.0",
- "jest-get-type": "^25.2.1",
- "jest-matcher-utils": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-regex-util": "^25.2.1"
+ "jest-get-type": "^25.2.6",
+ "jest-matcher-utils": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-regex-util": "^25.2.6"
},
"dependencies": {
"ansi-styles": {
}
}
},
- "extsprintf": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
- "dev": true
- },
- "falafel": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz",
- "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==",
+ "extract-zip": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz",
+ "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==",
"dev": true,
"requires": {
- "acorn": "^7.1.1",
- "foreach": "^2.0.5",
- "isarray": "^2.0.1",
- "object-keys": "^1.0.6"
+ "concat-stream": "^1.6.2",
+ "debug": "^2.6.9",
+ "mkdirp": "^0.5.4",
+ "yauzl": "^2.10.0"
},
"dependencies": {
- "acorn": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
- "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
- "dev": true
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
},
- "isarray": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
- "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
"fancy-log": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
"bser": "2.1.1"
}
},
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
"figgy-pudding": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
},
"dependencies": {
"ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "version": "6.12.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+ "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"dev": true
},
"schema-utils": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
- "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "version": "2.6.6",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
+ "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
"dev": true,
"requires": {
"ajv": "^6.12.0",
}
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"fontkit": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.8.0.tgz",
- "integrity": "sha512-EFDRCca7khfQWYu1iFhsqeABpi87f03MBdkT93ZE6YhqCdMzb5Eojb6c4dlJikGv5liuhByyzA7ikpIPTSBWbQ==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.8.1.tgz",
+ "integrity": "sha512-BsNCjDoYRxmNWFdAuK1y9bQt+igIxGtTC9u/jSFjR9MKhmI00rP1fwSvERt+5ddE82544l0XH5mzXozQVUy2Tw==",
"dev": true,
"requires": {
- "babel-runtime": "^6.11.6",
- "brfs": "^1.4.0",
+ "babel-runtime": "^6.26.0",
+ "brfs": "^2.0.0",
"brotli": "^1.2.0",
- "browserify-optional": "^1.0.0",
- "clone": "^1.0.1",
+ "browserify-optional": "^1.0.1",
+ "clone": "^1.0.4",
"deep-equal": "^1.0.0",
- "dfa": "^1.0.0",
+ "dfa": "^1.2.0",
"restructure": "^0.5.3",
"tiny-inflate": "^1.0.2",
- "unicode-properties": "^1.0.0",
+ "unicode-properties": "^1.2.2",
"unicode-trie": "^0.3.0"
},
"dependencies": {
- "brfs": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz",
- "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==",
- "dev": true,
- "requires": {
- "quote-stream": "^1.0.1",
- "resolve": "^1.1.5",
- "static-module": "^2.2.0",
- "through2": "^2.0.0"
- }
- },
"clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
- "magic-string": {
- "version": "0.22.5",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
- "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
- "dev": true,
- "requires": {
- "vlq": "^0.2.2"
- }
- },
- "merge-source-map": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz",
- "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=",
- "dev": true,
- "requires": {
- "source-map": "^0.5.6"
- }
- },
- "object-inspect": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz",
- "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==",
- "dev": true
- },
"pako": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
"integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
"dev": true
},
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
- },
- "static-module": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz",
- "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==",
- "dev": true,
- "requires": {
- "concat-stream": "~1.6.0",
- "convert-source-map": "^1.5.1",
- "duplexer2": "~0.1.4",
- "escodegen": "~1.9.0",
- "falafel": "^2.1.0",
- "has": "^1.0.1",
- "magic-string": "^0.22.4",
- "merge-source-map": "1.0.4",
- "object-inspect": "~1.4.0",
- "quote-stream": "~1.0.2",
- "readable-stream": "~2.3.3",
- "shallow-copy": "~0.0.1",
- "static-eval": "^2.0.0",
- "through2": "~2.0.3"
- }
- },
"unicode-trie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz",
"for-in": "^1.0.1"
}
},
- "foreach": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
- "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
- "dev": true
- },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
"dev": true
},
+ "getos": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/getos/-/getos-3.1.4.tgz",
+ "integrity": "sha512-UORPzguEB/7UG5hqiZai8f0vQ7hzynMQyJLxStoQ8dPGAcmgsfXOPA4iE/fGtweHYkK+z4zc9V0g+CIFRf5HYw==",
+ "dev": true,
+ "requires": {
+ "async": "^3.1.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
+ "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==",
+ "dev": true
+ }
+ }
+ },
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
"dev": true
},
+ "global-dirs": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.4"
+ }
+ },
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"dev": true
},
"handlebars": {
- "version": "4.7.3",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.3.tgz",
- "integrity": "sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==",
+ "version": "4.7.6",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+ "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
"dev": true,
"requires": {
+ "minimist": "^1.2.5",
"neo-async": "^2.6.0",
- "optimist": "^0.6.1",
"source-map": "^0.6.1",
- "uglify-js": "^3.1.4"
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
},
"dependencies": {
"source-map": {
}
},
"html-entities": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
- "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+ "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
"dev": true
},
"html-escaper": {
"dev": true,
"optional": true
},
- "immediate": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
- "dev": true
- },
"import-cwd": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
+ "indent-string": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
+ "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
+ "dev": true
+ },
"infer-owner": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
"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=",
+ "dev": true,
+ "requires": {
+ "global-dirs": "^0.1.0",
+ "is-path-inside": "^1.0.0"
+ },
+ "dependencies": {
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ }
+ }
+ },
"is-negated-glob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
+ "is-observable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz",
+ "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==",
+ "dev": true,
+ "requires": {
+ "symbol-observable": "^1.1.0"
+ }
+ },
"is-path-cwd": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
}
},
"is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
"dev": true
},
"is-regex": {
"dev": true
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
"istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-Vm9xwCiQ8t2cNNnckyeAV0UdxKpcQUz4nMxsBvIu8n2kmPSiyb5uaF/8LpmKr+yqL/MdOXaX2Nmdo4Qyxium9Q==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
"dev": true,
"requires": {
"html-escaper": "^2.0.0",
"istanbul-lib-report": "^3.0.0"
}
},
- "jasmine": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
- "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=",
- "dev": true,
- "requires": {
- "exit": "^0.1.2",
- "glob": "^7.0.6",
- "jasmine-core": "~2.8.0"
- },
- "dependencies": {
- "jasmine-core": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
- "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
- "dev": true
- }
- }
- },
- "jasmine-core": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz",
- "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==",
- "dev": true
- },
- "jasmine-fail-fast": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/jasmine-fail-fast/-/jasmine-fail-fast-2.0.1.tgz",
- "integrity": "sha512-En8ONwvDQOV+jyiZEZvbvUSLWSdJFj9HiWjhLdGq/V/gxs4XyST730ooe928BbRxv4bfy05OpykKuoOU4aLC5w==",
- "dev": true,
- "requires": {
- "lodash": "^4.17.15"
- }
- },
- "jasmine-spec-reporter": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.1.tgz",
- "integrity": "sha512-RrOZ+bSPnbk1/9KKs5lm0Nl0cqDCh/XXVlCmu3nkhEJH6HTDh4hoJZu3q8e9aq37C0eXEf/JEJnYy+t4m3arZQ==",
- "dev": true,
- "requires": {
- "colors": "1.4.0"
- }
- },
- "jasminewd2": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
- "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
- "dev": true
- },
"jest": {
"version": "25.2.4",
"resolved": "https://registry.npmjs.org/jest/-/jest-25.2.4.tgz",
"dev": true
},
"jest-cli": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.2.4.tgz",
- "integrity": "sha512-zeY2pRDWKj2LZudIncvvguwLMEdcnJqc2jJbwza1beqi80qqLvkPF/BjbFkK2sIV3r+mfTJS+7ITrvK6pCdRjg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-25.4.0.tgz",
+ "integrity": "sha512-usyrj1lzCJZMRN1r3QEdnn8e6E6yCx/QN7+B1sLoA68V7f3WlsxSSQfy0+BAwRiF4Hz2eHauf11GZG3PIfWTXQ==",
"dev": true,
"requires": {
- "@jest/core": "^25.2.4",
- "@jest/test-result": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/core": "^25.4.0",
+ "@jest/test-result": "^25.4.0",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
"exit": "^0.1.2",
"import-local": "^3.0.2",
"is-ci": "^2.0.0",
- "jest-config": "^25.2.4",
- "jest-util": "^25.2.3",
- "jest-validate": "^25.2.3",
+ "jest-config": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-validate": "^25.4.0",
"prompts": "^2.0.1",
"realpath-native": "^2.0.0",
"yargs": "^15.3.1"
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
}
},
"jest-changed-files": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.2.3.tgz",
- "integrity": "sha512-EFxy94dvvbqRB36ezIPLKJ4fDIC+jAdNs8i8uTwFpaXd6H3LVc3ova1lNS4ZPWk09OCR2vq5kSdSQgar7zMORg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-25.4.0.tgz",
+ "integrity": "sha512-VR/rfJsEs4BVMkwOTuStRyS630fidFVekdw/lBaBQjx9KK3VZFOZ2c0fsom2fRp8pMCrCTP6LGna00o/DXGlqA==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"execa": "^3.2.0",
"throat": "^5.0.0"
},
"dependencies": {
"cross-spawn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
- "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
+ "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
}
},
"jest-config": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.2.4.tgz",
- "integrity": "sha512-fxy3nIpwJqOUQJRVF/q+pNQb6dv5b9YufOeCbpPZJ/md1zXpiupbhfehpfODhnKOfqbzSiigtSLzlWWmbRxnqQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.4.0.tgz",
+ "integrity": "sha512-egT9aKYxMyMSQV1aqTgam0SkI5/I2P9qrKexN5r2uuM2+68ypnc+zPGmfUxK7p1UhE7dYH9SLBS7yb+TtmT1AA==",
"dev": true,
"requires": {
"@babel/core": "^7.1.0",
- "@jest/test-sequencer": "^25.2.4",
- "@jest/types": "^25.2.3",
- "babel-jest": "^25.2.4",
+ "@jest/test-sequencer": "^25.4.0",
+ "@jest/types": "^25.4.0",
+ "babel-jest": "^25.4.0",
"chalk": "^3.0.0",
"deepmerge": "^4.2.2",
"glob": "^7.1.1",
- "jest-environment-jsdom": "^25.2.4",
- "jest-environment-node": "^25.2.4",
- "jest-get-type": "^25.2.1",
- "jest-jasmine2": "^25.2.4",
- "jest-regex-util": "^25.2.1",
- "jest-resolve": "^25.2.3",
- "jest-util": "^25.2.3",
- "jest-validate": "^25.2.3",
+ "jest-environment-jsdom": "^25.4.0",
+ "jest-environment-node": "^25.4.0",
+ "jest-get-type": "^25.2.6",
+ "jest-jasmine2": "^25.4.0",
+ "jest-regex-util": "^25.2.6",
+ "jest-resolve": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-validate": "^25.4.0",
"micromatch": "^4.0.2",
- "pretty-format": "^25.2.3",
+ "pretty-format": "^25.4.0",
"realpath-native": "^2.0.0"
},
"dependencies": {
}
},
"jest-diff": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.2.3.tgz",
- "integrity": "sha512-VtZ6LAQtaQpFsmEzps15dQc5ELbJxy4L2DOSo2Ev411TUEtnJPkAMD7JneVypeMJQ1y3hgxN9Ao13n15FAnavg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.4.0.tgz",
+ "integrity": "sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
- "diff-sequences": "^25.2.1",
- "jest-get-type": "^25.2.1",
- "pretty-format": "^25.2.3"
+ "diff-sequences": "^25.2.6",
+ "jest-get-type": "^25.2.6",
+ "pretty-format": "^25.4.0"
},
"dependencies": {
"ansi-styles": {
}
},
"jest-docblock": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.2.3.tgz",
- "integrity": "sha512-d3/tmjLLrH5fpRGmIm3oFa3vOaD/IjPxtXVOrfujpfJ9y1tCDB1x/tvunmdOVAyF03/xeMwburl6ITbiQT1mVA==",
+ "version": "25.3.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.3.0.tgz",
+ "integrity": "sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg==",
"dev": true,
"requires": {
"detect-newline": "^3.0.0"
}
},
"jest-each": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.2.3.tgz",
- "integrity": "sha512-RTlmCjsBDK2c9T5oO4MqccA3/5Y8BUtiEy7OOQik1iyCgdnNdHbI0pNEpyapZPBG0nlvZ4mIu7aY6zNUvLraAQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.4.0.tgz",
+ "integrity": "sha512-lwRIJ8/vQU/6vq3nnSSUw1Y3nz5tkYSFIywGCZpUBd6WcRgpn8NmJoQICojbpZmsJOJNHm0BKdyuJ6Xdx+eDQQ==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
- "jest-get-type": "^25.2.1",
- "jest-util": "^25.2.3",
- "pretty-format": "^25.2.3"
+ "jest-get-type": "^25.2.6",
+ "jest-util": "^25.4.0",
+ "pretty-format": "^25.4.0"
},
"dependencies": {
"ansi-styles": {
}
},
"jest-environment-jsdom": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.2.4.tgz",
- "integrity": "sha512-5dm+tNwrLmhELdjAwiQnVGf/U9iFMWdTL4/wyrMg2HU6RQnCiuxpWbIigLHUhuP1P2Ak0F4k3xhjrikboKyShA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.4.0.tgz",
+ "integrity": "sha512-KTitVGMDrn2+pt7aZ8/yUTuS333w3pWt1Mf88vMntw7ZSBNDkRS6/4XLbFpWXYfWfp1FjcjQTOKzbK20oIehWQ==",
"dev": true,
"requires": {
- "@jest/environment": "^25.2.4",
- "@jest/fake-timers": "^25.2.4",
- "@jest/types": "^25.2.3",
- "jest-mock": "^25.2.3",
- "jest-util": "^25.2.3",
+ "@jest/environment": "^25.4.0",
+ "@jest/fake-timers": "^25.4.0",
+ "@jest/types": "^25.4.0",
+ "jest-mock": "^25.4.0",
+ "jest-util": "^25.4.0",
"jsdom": "^15.2.1"
}
},
"jest-environment-node": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.2.4.tgz",
- "integrity": "sha512-Jkc5Y8goyXPrLRHnrUlqC7P4o5zn2m4zw6qWoRJ59kxV1f2a5wK+TTGhrhCwnhW/Ckpdl/pm+LufdvhJkvJbiw==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.4.0.tgz",
+ "integrity": "sha512-wryZ18vsxEAKFH7Z74zi/y/SyI1j6UkVZ6QsllBuT/bWlahNfQjLNwFsgh/5u7O957dYFoXj4yfma4n4X6kU9A==",
"dev": true,
"requires": {
- "@jest/environment": "^25.2.4",
- "@jest/fake-timers": "^25.2.4",
- "@jest/types": "^25.2.3",
- "jest-mock": "^25.2.3",
- "jest-util": "^25.2.3",
+ "@jest/environment": "^25.4.0",
+ "@jest/fake-timers": "^25.4.0",
+ "@jest/types": "^25.4.0",
+ "jest-mock": "^25.4.0",
+ "jest-util": "^25.4.0",
"semver": "^6.3.0"
}
},
"jest-get-type": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.1.tgz",
- "integrity": "sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w==",
+ "version": "25.2.6",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz",
+ "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==",
"dev": true
},
"jest-haste-map": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.2.3.tgz",
- "integrity": "sha512-pAP22OHtPr4qgZlJJFks2LLgoQUr4XtM1a+F5UaPIZNiCRnePA0hM3L7aiJ0gzwiNIYwMTfKRwG/S1L28J3A3A==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.4.0.tgz",
+ "integrity": "sha512-5EoCe1gXfGC7jmXbKzqxESrgRcaO3SzWXGCnvp9BcT0CFMyrB1Q6LIsjl9RmvmJGQgW297TCfrdgiy574Rl9HQ==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"anymatch": "^3.0.3",
"fb-watchman": "^2.0.0",
"fsevents": "2.1.2",
"graceful-fs": "^4.2.3",
- "jest-serializer": "^25.2.1",
- "jest-util": "^25.2.3",
- "jest-worker": "^25.2.1",
+ "jest-serializer": "^25.2.6",
+ "jest-util": "^25.4.0",
+ "jest-worker": "^25.4.0",
"micromatch": "^4.0.2",
"sane": "^4.0.3",
"walker": "^1.0.7",
"dev": true
},
"jest-worker": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz",
- "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz",
+ "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
}
},
"jest-jasmine2": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.2.4.tgz",
- "integrity": "sha512-juoKrmNmLwaheNbAg71SuUF9ovwUZCFNTpKVhvCXWk+SSeORcIUMptKdPCoLXV3D16htzhTSKmNxnxSk4SrTjA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.4.0.tgz",
+ "integrity": "sha512-QccxnozujVKYNEhMQ1vREiz859fPN/XklOzfQjm2j9IGytAkUbSwjFRBtQbHaNZ88cItMpw02JnHGsIdfdpwxQ==",
"dev": true,
"requires": {
"@babel/traverse": "^7.1.0",
- "@jest/environment": "^25.2.4",
- "@jest/source-map": "^25.2.1",
- "@jest/test-result": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/environment": "^25.4.0",
+ "@jest/source-map": "^25.2.6",
+ "@jest/test-result": "^25.4.0",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
"co": "^4.6.0",
- "expect": "^25.2.4",
+ "expect": "^25.4.0",
"is-generator-fn": "^2.0.0",
- "jest-each": "^25.2.3",
- "jest-matcher-utils": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-runtime": "^25.2.4",
- "jest-snapshot": "^25.2.4",
- "jest-util": "^25.2.3",
- "pretty-format": "^25.2.3",
+ "jest-each": "^25.4.0",
+ "jest-matcher-utils": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-runtime": "^25.4.0",
+ "jest-snapshot": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "pretty-format": "^25.4.0",
"throat": "^5.0.0"
},
"dependencies": {
}
},
"jest-leak-detector": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.2.3.tgz",
- "integrity": "sha512-yblCMPE7NJKl7778Cf/73yyFWAas5St0iiEBwq7RDyaz6Xd4WPFnPz2j7yDb/Qce71A1IbDoLADlcwD8zT74Aw==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.4.0.tgz",
+ "integrity": "sha512-7Y6Bqfv2xWsB+7w44dvZuLs5SQ//fzhETgOGG7Gq3TTGFdYvAgXGwV8z159RFZ6fXiCPm/szQ90CyfVos9JIFQ==",
"dev": true,
"requires": {
- "jest-get-type": "^25.2.1",
- "pretty-format": "^25.2.3"
+ "jest-get-type": "^25.2.6",
+ "pretty-format": "^25.4.0"
}
},
"jest-matcher-utils": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.2.3.tgz",
- "integrity": "sha512-ZmiXiwQRVM9MoKjGMP5YsGGk2Th5ncyRxfXKz5AKsmU8m43kgNZirckVzaP61MlSa9LKmXbevdYqVp1ZKAw2Rw==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.4.0.tgz",
+ "integrity": "sha512-yPMdtj7YDgXhnGbc66bowk8AkQ0YwClbbwk3Kzhn5GVDrciiCr27U4NJRbrqXbTdtxjImONITg2LiRIw650k5A==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
- "jest-diff": "^25.2.3",
- "jest-get-type": "^25.2.1",
- "pretty-format": "^25.2.3"
+ "jest-diff": "^25.4.0",
+ "jest-get-type": "^25.2.6",
+ "pretty-format": "^25.4.0"
},
"dependencies": {
"ansi-styles": {
}
},
"jest-message-util": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.2.4.tgz",
- "integrity": "sha512-9wWMH3Bf+GVTv0GcQLmH/FRr0x0toptKw9TA8U5YFLVXx7Tq9pvcNzTyJrcTJ+wLqNbMPPJlJNft4MnlcrtF5Q==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.4.0.tgz",
+ "integrity": "sha512-LYY9hRcVGgMeMwmdfh9tTjeux1OjZHMusq/E5f3tJN+dAoVVkJtq5ZUEPIcB7bpxDUt2zjUsrwg0EGgPQ+OhXQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
- "@jest/test-result": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"@types/stack-utils": "^1.0.1",
"chalk": "^3.0.0",
"micromatch": "^4.0.2",
}
},
"jest-mock": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.2.3.tgz",
- "integrity": "sha512-xlf+pyY0j47zoCs8zGGOGfWyxxLximE8YFOfEK8s4FruR8DtM/UjNj61um+iDuMAFEBDe1bhCXkqiKoCmWjJzg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.4.0.tgz",
+ "integrity": "sha512-MdazSfcYAUjJjuVTTnusLPzE0pE4VXpOUzWdj8sbM+q6abUjm3bATVPXFqTXrxSieR8ocpvQ9v/QaQCftioQFg==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3"
+ "@jest/types": "^25.4.0"
}
},
"jest-pnp-resolver": {
}
},
"jest-regex-util": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.1.tgz",
- "integrity": "sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w==",
+ "version": "25.2.6",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz",
+ "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==",
"dev": true
},
"jest-resolve": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.2.3.tgz",
- "integrity": "sha512-1vZMsvM/DBH258PnpUNSXIgtzpYz+vCVCj9+fcy4akZl4oKbD+9hZSlfe9RIDpU0Fc28ozHQrmwX3EqFRRIHGg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.4.0.tgz",
+ "integrity": "sha512-wOsKqVDFWUiv8BtLMCC6uAJ/pHZkfFgoBTgPtmYlsprAjkxrr2U++ZnB3l5ykBMd2O24lXvf30SMAjJIW6k2aA==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"browser-resolve": "^1.11.3",
"chalk": "^3.0.0",
"jest-pnp-resolver": "^1.2.1",
+ "read-pkg-up": "^7.0.1",
"realpath-native": "^2.0.0",
- "resolve": "^1.15.1"
+ "resolve": "^1.15.1",
+ "slash": "^3.0.0"
},
"dependencies": {
"ansi-styles": {
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
}
},
"jest-resolve-dependencies": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.2.4.tgz",
- "integrity": "sha512-qhUnK4PfNHzNdca7Ub1mbAqE0j5WNyMTwxBZZJjQlUrdqsiYho/QGK65FuBkZuSoYtKIIqriR9TpGrPEc3P5Gg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-25.4.0.tgz",
+ "integrity": "sha512-A0eoZXx6kLiuG1Ui7wITQPl04HwjLErKIJTt8GR3c7UoDAtzW84JtCrgrJ6Tkw6c6MwHEyAaLk7dEPml5pf48A==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
- "jest-regex-util": "^25.2.1",
- "jest-snapshot": "^25.2.4"
+ "@jest/types": "^25.4.0",
+ "jest-regex-util": "^25.2.6",
+ "jest-snapshot": "^25.4.0"
}
},
"jest-runner": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.2.4.tgz",
- "integrity": "sha512-5xaIfqqxck9Wg2CV4b9KmJtf/sWO7zWQx7O+34GCLGPzoPcVmB3mZtdrQI1/jS3Reqjru9ycLjgLHSf6XoxRqA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.4.0.tgz",
+ "integrity": "sha512-wWQSbVgj2e/1chFdMRKZdvlmA6p1IPujhpLT7TKNtCSl1B0PGBGvJjCaiBal/twaU2yfk8VKezHWexM8IliBfA==",
"dev": true,
"requires": {
- "@jest/console": "^25.2.3",
- "@jest/environment": "^25.2.4",
- "@jest/test-result": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/console": "^25.4.0",
+ "@jest/environment": "^25.4.0",
+ "@jest/test-result": "^25.4.0",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
"exit": "^0.1.2",
"graceful-fs": "^4.2.3",
- "jest-config": "^25.2.4",
- "jest-docblock": "^25.2.3",
- "jest-haste-map": "^25.2.3",
- "jest-jasmine2": "^25.2.4",
- "jest-leak-detector": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-resolve": "^25.2.3",
- "jest-runtime": "^25.2.4",
- "jest-util": "^25.2.3",
- "jest-worker": "^25.2.1",
+ "jest-config": "^25.4.0",
+ "jest-docblock": "^25.3.0",
+ "jest-haste-map": "^25.4.0",
+ "jest-jasmine2": "^25.4.0",
+ "jest-leak-detector": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-resolve": "^25.4.0",
+ "jest-runtime": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-worker": "^25.4.0",
"source-map-support": "^0.5.6",
"throat": "^5.0.0"
},
"dev": true
},
"jest-worker": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.1.tgz",
- "integrity": "sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.4.0.tgz",
+ "integrity": "sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==",
"dev": true,
"requires": {
"merge-stream": "^2.0.0",
}
},
"jest-runtime": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.2.4.tgz",
- "integrity": "sha512-6ehOUizgIghN+aV5YSrDzTZ+zJ9omgEjJbTHj3Jqes5D52XHfhzT7cSfdREwkNjRytrR7mNwZ7pRauoyNLyJ8Q==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.4.0.tgz",
+ "integrity": "sha512-lgNJlCDULtXu9FumnwCyWlOub8iytijwsPNa30BKrSNtgoT6NUMXOPrZvsH06U6v0wgD/Igwz13nKA2wEKU2VA==",
"dev": true,
"requires": {
- "@jest/console": "^25.2.3",
- "@jest/environment": "^25.2.4",
- "@jest/source-map": "^25.2.1",
- "@jest/test-result": "^25.2.4",
- "@jest/transform": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/console": "^25.4.0",
+ "@jest/environment": "^25.4.0",
+ "@jest/source-map": "^25.2.6",
+ "@jest/test-result": "^25.4.0",
+ "@jest/transform": "^25.4.0",
+ "@jest/types": "^25.4.0",
"@types/yargs": "^15.0.0",
"chalk": "^3.0.0",
"collect-v8-coverage": "^1.0.0",
"exit": "^0.1.2",
"glob": "^7.1.3",
"graceful-fs": "^4.2.3",
- "jest-config": "^25.2.4",
- "jest-haste-map": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-mock": "^25.2.3",
- "jest-regex-util": "^25.2.1",
- "jest-resolve": "^25.2.3",
- "jest-snapshot": "^25.2.4",
- "jest-util": "^25.2.3",
- "jest-validate": "^25.2.3",
+ "jest-config": "^25.4.0",
+ "jest-haste-map": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-mock": "^25.4.0",
+ "jest-regex-util": "^25.2.6",
+ "jest-resolve": "^25.4.0",
+ "jest-snapshot": "^25.4.0",
+ "jest-util": "^25.4.0",
+ "jest-validate": "^25.4.0",
"realpath-native": "^2.0.0",
"slash": "^3.0.0",
"strip-bom": "^4.0.0",
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
}
},
"jest-serializer": {
- "version": "25.2.1",
- "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.1.tgz",
- "integrity": "sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ==",
+ "version": "25.2.6",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.6.tgz",
+ "integrity": "sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ==",
"dev": true
},
"jest-silent-reporter": {
}
},
"jest-snapshot": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.2.4.tgz",
- "integrity": "sha512-nIwpW7FZCq5p0AE3Oyqyb6jL0ENJixXzJ5/CD/XRuOqp3gS5OM3O/k+NnTrniCXxPFV4ry6s9HNfiPQBi0wcoA==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.4.0.tgz",
+ "integrity": "sha512-J4CJ0X2SaGheYRZdLz9CRHn9jUknVmlks4UBeu270hPAvdsauFXOhx9SQP2JtRzhnR3cvro/9N9KP83/uvFfRg==",
"dev": true,
"requires": {
"@babel/types": "^7.0.0",
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"@types/prettier": "^1.19.0",
"chalk": "^3.0.0",
- "expect": "^25.2.4",
- "jest-diff": "^25.2.3",
- "jest-get-type": "^25.2.1",
- "jest-matcher-utils": "^25.2.3",
- "jest-message-util": "^25.2.4",
- "jest-resolve": "^25.2.3",
+ "expect": "^25.4.0",
+ "jest-diff": "^25.4.0",
+ "jest-get-type": "^25.2.6",
+ "jest-matcher-utils": "^25.4.0",
+ "jest-message-util": "^25.4.0",
+ "jest-resolve": "^25.4.0",
"make-dir": "^3.0.0",
"natural-compare": "^1.4.0",
- "pretty-format": "^25.2.3",
+ "pretty-format": "^25.4.0",
"semver": "^6.3.0"
},
"dependencies": {
"dev": true
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
"jest-util": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.2.3.tgz",
- "integrity": "sha512-7tWiMICVSo9lNoObFtqLt9Ezt5exdFlWs5fLe1G4XLY2lEbZc814cw9t4YHScqBkWMfzth8ASHKlYBxiX2rdCw==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.4.0.tgz",
+ "integrity": "sha512-WSZD59sBtAUjLv1hMeKbNZXmMcrLRWcYqpO8Dz8b4CeCTZpfNQw2q9uwrYAD+BbJoLJlu4ezVPwtAmM/9/SlZA==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"chalk": "^3.0.0",
"is-ci": "^2.0.0",
"make-dir": "^3.0.0"
"dev": true
},
"make-dir": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz",
- "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"requires": {
"semver": "^6.0.0"
}
},
"jest-validate": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.2.3.tgz",
- "integrity": "sha512-GObn91jzU0B0Bv4cusAwjP6vnWy78hJUM8MOSz7keRfnac/ZhQWIsUjvk01IfeXNTemCwgR57EtdjQMzFZGREg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.4.0.tgz",
+ "integrity": "sha512-hvjmes/EFVJSoeP1yOl8qR8mAtMR3ToBkZeXrD/ZS9VxRyWDqQ/E1C5ucMTeSmEOGLipvdlyipiGbHJ+R1MQ0g==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"camelcase": "^5.3.1",
"chalk": "^3.0.0",
- "jest-get-type": "^25.2.1",
+ "jest-get-type": "^25.2.6",
"leven": "^3.1.0",
- "pretty-format": "^25.2.3"
+ "pretty-format": "^25.4.0"
},
"dependencies": {
"ansi-styles": {
}
},
"jest-watcher": {
- "version": "25.2.4",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.2.4.tgz",
- "integrity": "sha512-p7g7s3zqcy69slVzQYcphyzkB2FBmJwMbv6k6KjI5mqd6KnUnQPfQVKuVj2l+34EeuxnbXqnrjtUFmxhcL87rg==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-25.4.0.tgz",
+ "integrity": "sha512-36IUfOSRELsKLB7k25j/wutx0aVuHFN6wO94gPNjQtQqFPa2rkOymmx9rM5EzbF3XBZZ2oqD9xbRVoYa2w86gw==",
"dev": true,
"requires": {
- "@jest/test-result": "^25.2.4",
- "@jest/types": "^25.2.3",
+ "@jest/test-result": "^25.4.0",
+ "@jest/types": "^25.4.0",
"ansi-escapes": "^4.2.1",
"chalk": "^3.0.0",
- "jest-util": "^25.2.3",
+ "jest-util": "^25.4.0",
"string-length": "^3.1.0"
},
"dependencies": {
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
"dev": true
},
- "escodegen": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
- "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
- "dev": true,
- "requires": {
- "esprima": "^4.0.1",
- "estraverse": "^4.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.6.1"
- }
- },
"parse5": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true
- },
"tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
}
},
"ws": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
- "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==",
+ "version": "7.2.5",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
+ "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==",
"dev": true
}
}
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.0"
}
},
"jsonfile": {
"verror": "1.10.0"
}
},
- "jszip": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.2.tgz",
- "integrity": "sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA==",
- "dev": true,
- "requires": {
- "lie": "~3.3.0",
- "pako": "~1.0.2",
- "readable-stream": "~2.3.6",
- "set-immediate-shim": "~1.0.1"
- }
- },
"karma-source-map-support": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
- "klaw-sync": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
- "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.11"
- }
- },
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true
},
+ "lazy-ass": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
+ "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=",
+ "dev": true
+ },
"lcid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
"webpack-sources": "^1.2.0"
}
},
- "lie": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
- "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
- "dev": true,
- "requires": {
- "immediate": "~3.0.5"
- }
- },
"liftoff": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
}
}
},
+ "lines-and-columns": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
+ "dev": true
+ },
+ "listr": {
+ "version": "0.14.3",
+ "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz",
+ "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==",
+ "dev": true,
+ "requires": {
+ "@samverschueren/stream-to-observable": "^0.3.0",
+ "is-observable": "^1.1.0",
+ "is-promise": "^2.1.0",
+ "is-stream": "^1.1.0",
+ "listr-silent-renderer": "^1.1.1",
+ "listr-update-renderer": "^0.5.0",
+ "listr-verbose-renderer": "^0.5.0",
+ "p-map": "^2.0.0",
+ "rxjs": "^6.3.3"
+ }
+ },
+ "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=",
+ "dev": true
+ },
+ "listr-update-renderer": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz",
+ "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "cli-truncate": "^0.2.1",
+ "elegant-spinner": "^1.0.1",
+ "figures": "^1.7.0",
+ "indent-string": "^3.0.0",
+ "log-symbols": "^1.0.2",
+ "log-update": "^2.3.0",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "log-symbols": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz",
+ "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "listr-verbose-renderer": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz",
+ "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "cli-cursor": "^2.1.0",
+ "date-fns": "^1.27.2",
+ "figures": "^2.0.0"
+ },
+ "dependencies": {
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ }
+ }
+ },
"live-server": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/live-server/-/live-server-1.2.1.tgz",
}
},
"minimist": {
+ "version": "1.2.5",
+ "bundled": true,
"dev": true,
- "optional": true,
- "version": "1.2.5"
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"dev": true,
"optional": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"ms": {
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
"readable-stream": {
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
},
+ "lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=",
+ "dev": true
+ },
"lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
+ "log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2"
+ }
+ },
+ "log-update": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz",
+ "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "cli-cursor": "^2.0.0",
+ "wrap-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz",
+ "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0"
+ }
+ }
+ }
+ },
"loglevel": {
- "version": "1.6.7",
- "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz",
- "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
+ "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
"dev": true
},
"loglevel-plugin-prefix": {
"dev": true
},
"mime-db": {
- "version": "1.43.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
- "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"dev": true
},
"mime-types": {
- "version": "2.1.26",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
- "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"dev": true,
"requires": {
- "mime-db": "1.43.0"
+ "mime-db": "1.44.0"
}
},
"mimic-fn": {
}
},
"minimist": {
- "dev": true,
- "version": "1.2.5"
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
},
"minipass": {
"version": "2.9.0",
}
},
"mkdirp": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
- "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"mobx": {
"which": "^1.2.9"
}
},
+ "read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ }
+ },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"dev": true
},
"object-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
- "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
- "dev": true
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
+ "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
},
"object-keys": {
"version": "1.1.1",
"is-wsl": "^1.1.0"
}
},
- "optimist": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
- "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
- "dev": true,
- "requires": {
- "minimist": "1.2.5",
- "wordwrap": "~0.0.2"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
- }
- },
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"os-tmpdir": "^1.0.0"
}
},
+ "ospath": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
+ "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=",
+ "dev": true
+ },
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
}
}
},
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
"integrity": "sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==",
"dev": true
},
+ "pretty-bytes": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz",
+ "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==",
+ "dev": true
+ },
"pretty-format": {
- "version": "25.2.3",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.2.3.tgz",
- "integrity": "sha512-IP4+5UOAVGoyqC/DiomOeHBUKN6q00gfyT2qpAsRH64tgOKB2yF7FHJXC18OCiU0/YFierACup/zdCOWw0F/0w==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.4.0.tgz",
+ "integrity": "sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==",
"dev": true,
"requires": {
- "@jest/types": "^25.2.3",
+ "@jest/types": "^25.4.0",
"ansi-regex": "^5.0.0",
"ansi-styles": "^4.0.0",
"react-is": "^16.12.0"
"dev": true
},
"cross-spawn": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz",
- "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
+ "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"dev": true
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
"genfun": "^5.0.0"
}
},
- "protractor": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.3.tgz",
- "integrity": "sha512-7pMAolv8Ah1yJIqaorDTzACtn3gk7BamVKPTeO5lqIGOrfosjPgXFx/z1dqSI+m5EeZc2GMJHPr5DYlodujDNA==",
- "dev": true,
- "requires": {
- "@types/q": "^0.0.32",
- "@types/selenium-webdriver": "^3.0.0",
- "blocking-proxy": "^1.0.0",
- "browserstack": "^1.5.1",
- "chalk": "^1.1.3",
- "glob": "^7.0.3",
- "jasmine": "2.8.0",
- "jasminewd2": "^2.1.0",
- "optimist": "~0.6.0",
- "q": "1.4.1",
- "saucelabs": "^1.5.0",
- "selenium-webdriver": "3.6.0",
- "source-map-support": "~0.4.0",
- "webdriver-js-extender": "2.1.0",
- "webdriver-manager": "^12.0.6"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
- "dev": true
- },
- "chalk": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
- "dev": true,
- "requires": {
- "ansi-styles": "^2.2.1",
- "escape-string-regexp": "^1.0.2",
- "has-ansi": "^2.0.0",
- "strip-ansi": "^3.0.0",
- "supports-color": "^2.0.0"
- }
- },
- "del": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
- "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
- "dev": true,
- "requires": {
- "globby": "^5.0.0",
- "is-path-cwd": "^1.0.0",
- "is-path-in-cwd": "^1.0.0",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0",
- "rimraf": "^2.2.8"
- }
- },
- "globby": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
- "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
- "dev": true,
- "requires": {
- "array-union": "^1.0.1",
- "arrify": "^1.0.0",
- "glob": "^7.0.3",
- "object-assign": "^4.0.1",
- "pify": "^2.0.0",
- "pinkie-promise": "^2.0.0"
- }
- },
- "is-path-cwd": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
- "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
- "dev": true
- },
- "is-path-in-cwd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
- "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
- "dev": true,
- "requires": {
- "is-path-inside": "^1.0.0"
- }
- },
- "is-path-inside": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
- "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
- "dev": true,
- "requires": {
- "path-is-inside": "^1.0.1"
- }
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
- "dev": true
- },
- "semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
- "dev": true
- },
- "source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
- "dev": true
- },
- "source-map-support": {
- "version": "0.4.18",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
- "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
- "dev": true,
- "requires": {
- "source-map": "^0.5.6"
- }
- },
- "supports-color": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
- "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
- "dev": true
- },
- "webdriver-manager": {
- "version": "12.1.7",
- "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz",
- "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==",
- "dev": true,
- "requires": {
- "adm-zip": "^0.4.9",
- "chalk": "^1.1.1",
- "del": "^2.2.0",
- "glob": "^7.0.3",
- "ini": "^1.3.4",
- "minimist": "1.2.5",
- "q": "^1.4.1",
- "request": "^2.87.0",
- "rimraf": "^2.5.2",
- "semver": "^5.3.0",
- "xml2js": "^0.4.17"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
- }
- }
- }
- },
- "protractor-fail-fast": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/protractor-fail-fast/-/protractor-fail-fast-3.1.0.tgz",
- "integrity": "sha512-OjuIFmY7hm5R/Msmioyg3aBevySpmpIgtm2TGUvMEqTzviPk/Fqd1HYmMjIQ+NzFMzrK+93LJa4civDvw1+hEg==",
- "dev": true,
- "requires": {
- "jasmine-fail-fast": "~2.0.0"
- }
- },
- "protractor-screenshoter-plugin": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/protractor-screenshoter-plugin/-/protractor-screenshoter-plugin-0.10.3.tgz",
- "integrity": "sha512-OF9kGe1rMxBQY4uXzXQUFT14EB83rz8DlDcxmH5HcOHPBpUhGh+Nwo7+K87w1LoLcTuGdG7Bz+/hGwoGguDfsA==",
- "dev": true,
- "requires": {
- "circular-json": "^0.5.1",
- "fs-extra": "^7.0.0",
- "klaw-sync": "^6.0.0",
- "lodash": "^4.17.11",
- "mkdirp": "^0.5.1",
- "moment": "^2.20.1",
- "q": "^1.5.1",
- "screenshoter-report-analyzer": "^0.6",
- "uuid": "^3.1.0"
- },
- "dependencies": {
- "fs-extra": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
- "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
- "dev": true,
- "requires": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- }
- },
- "q": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
- "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
- "dev": true
- }
- }
- },
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
+ "ps-tree": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
+ "integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
+ "dev": true,
+ "requires": {
+ "event-stream": "=3.3.4"
+ }
+ },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
},
- "q": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
- "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
- "dev": true
- },
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"dev": true,
"requires": {
"buffer-equal": "0.0.1",
- "minimist": "1.2.5",
+ "minimist": "^1.1.3",
"through2": "^2.0.0"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
+ "ramda": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
+ "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==",
+ "dev": true
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
},
"dependencies": {
"ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "version": "6.12.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+ "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"dev": true
},
"schema-utils": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
- "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "version": "2.6.6",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
+ "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
"dev": true,
"requires": {
"ajv": "^6.12.0",
}
},
"read-pkg": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
- "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+ "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
"dev": true,
"requires": {
- "load-json-file": "^4.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^3.0.0"
+ "@types/normalize-package-data": "^2.4.0",
+ "normalize-package-data": "^2.5.0",
+ "parse-json": "^5.0.0",
+ "type-fest": "^0.6.0"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
+ "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1",
+ "lines-and-columns": "^1.1.6"
+ }
+ },
+ "type-fest": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+ "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+ "dev": true
+ }
+ }
+ },
+ "read-pkg-up": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+ "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.1.0",
+ "read-pkg": "^5.2.0",
+ "type-fest": "^0.8.1"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ }
}
},
"readable-stream": {
}
},
"readdirp": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
- "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true,
"requires": {
- "picomatch": "^2.0.7"
+ "picomatch": "^2.2.1"
}
},
"realpath-native": {
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"uuid": "^3.3.2"
}
},
+ "request-progress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+ "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=",
+ "dev": true,
+ "requires": {
+ "throttleit": "^1.0.0"
+ }
+ },
"request-promise-core": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
"dev": true
},
"run-async": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
- "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
- "dev": true,
- "requires": {
- "is-promise": "^2.1.0"
- }
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
},
"run-queue": {
"version": "1.0.3",
"execa": "^1.0.0",
"fb-watchman": "^2.0.0",
"micromatch": "^3.1.4",
- "minimist": "1.2.5",
+ "minimist": "^1.1.1",
"walker": "~1.0.5"
},
"dependencies": {
"pump": "^3.0.0"
}
},
- "minimist": {
- "version": "1.2.5"
- },
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
}
}
},
- "saucelabs": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
- "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
- "dev": true,
- "requires": {
- "https-proxy-agent": "^2.2.1"
- }
- },
"sax": {
"version": "0.5.8",
"resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
"get-assigned-identifiers": "^1.1.0"
}
},
- "screenshoter-report-analyzer": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/screenshoter-report-analyzer/-/screenshoter-report-analyzer-0.6.0.tgz",
- "integrity": "sha1-Cm+I1fXRrBa2z3Ji7/ujH+5I7RI=",
- "dev": true
- },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
"dev": true
},
- "selenium-webdriver": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
- "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
- "dev": true,
- "requires": {
- "jszip": "^3.1.3",
- "rimraf": "^2.5.4",
- "tmp": "0.0.30",
- "xml2js": "^0.4.17"
- },
- "dependencies": {
- "tmp": {
- "version": "0.0.30",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
- "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
- "dev": true,
- "requires": {
- "os-tmpdir": "~1.0.1"
- }
- }
- }
- },
"selfsigned": {
"version": "1.10.7",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
- "set-immediate-shim": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
- "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
- "dev": true
- },
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
"dev": true
},
+ "slice-ansi": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
+ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
+ "dev": true
+ },
"smart-buffer": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
}
},
"spdx-exceptions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
- "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
"dev": true
},
"spdy": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz",
- "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
"dev": true,
"requires": {
"debug": "^4.1.0",
"integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==",
"dev": true
},
- "static-eval": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.5.tgz",
- "integrity": "sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==",
+ "start-server-and-test": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-1.11.0.tgz",
+ "integrity": "sha512-FhkJFYL/lvbd0tKWvbxWNWjtFtq3Zpa09QDjA8EUH88AsgNL4hkAAKYNmbac+fFM8/GIZoJ1Mj4mm3SMI0X1bA==",
"dev": true,
"requires": {
- "escodegen": "^1.11.1"
+ "bluebird": "3.7.2",
+ "check-more-types": "2.24.0",
+ "debug": "4.1.1",
+ "execa": "3.4.0",
+ "lazy-ass": "1.6.0",
+ "ps-tree": "1.2.0",
+ "wait-on": "4.0.0"
},
"dependencies": {
- "escodegen": {
- "version": "1.14.1",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
- "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
+ "cross-spawn": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
+ "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
"dev": true,
"requires": {
- "esprima": "^4.0.1",
- "estraverse": "^4.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.6.1"
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
}
},
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "execa": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz",
+ "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==",
"dev": true,
- "optional": true
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "p-finally": "^2.0.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+ "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "is-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+ "dev": true
+ },
+ "npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.0.0"
+ }
+ },
+ "p-finally": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz",
+ "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
}
}
},
+ "static-eval": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.5.tgz",
+ "integrity": "sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==",
+ "dev": true,
+ "requires": {
+ "escodegen": "^1.11.1"
+ }
+ },
"static-extend": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
}
},
"static-module": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/static-module/-/static-module-3.0.3.tgz",
- "integrity": "sha512-RDaMYaI5o/ym0GkCqL/PlD1Pn216omp8fY81okxZ6f6JQxWW5tptOw9reXoZX85yt/scYvbWIt6uoszeyf+/MQ==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/static-module/-/static-module-3.0.4.tgz",
+ "integrity": "sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw==",
"dev": true,
"requires": {
"acorn-node": "^1.3.0",
"concat-stream": "~1.6.0",
"convert-source-map": "^1.5.1",
"duplexer2": "~0.1.4",
- "escodegen": "~1.9.0",
+ "escodegen": "^1.11.1",
"has": "^1.0.1",
- "magic-string": "^0.22.4",
+ "magic-string": "0.25.1",
"merge-source-map": "1.0.4",
- "object-inspect": "~1.4.0",
+ "object-inspect": "^1.6.0",
"readable-stream": "~2.3.3",
"scope-analyzer": "^2.0.1",
"shallow-copy": "~0.0.1",
- "static-eval": "^2.0.2",
+ "static-eval": "^2.0.5",
"through2": "~2.0.3"
},
"dependencies": {
"magic-string": {
- "version": "0.22.5",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz",
- "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==",
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz",
+ "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==",
"dev": true,
"requires": {
- "vlq": "^0.2.2"
+ "sourcemap-codec": "^1.4.1"
}
},
"merge-source-map": {
"source-map": "^0.5.6"
}
},
- "object-inspect": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz",
- "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==",
- "dev": true
- },
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
}
},
"string.prototype.trimend": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz",
- "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
}
},
"string.prototype.trimstart": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz",
- "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
},
"dependencies": {
"ajv": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
- "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+ "version": "6.12.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+ "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"dev": true
},
"schema-utils": {
- "version": "2.6.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
- "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
+ "version": "2.6.6",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.6.tgz",
+ "integrity": "sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA==",
"dev": true,
"requires": {
"ajv": "^6.12.0",
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"dev": true
},
+ "throttleit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+ "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
+ "dev": true
+ },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"dev": true,
"requires": {
"colors": "^1.0.3",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"prompts": "^2.0.4",
"request": "^2.88.0",
"request-promise-native": "^1.0.7",
"xliff": "^4.2.0"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
"traverse": {
"dev": true
},
"ts-jest": {
- "version": "25.3.0",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.3.0.tgz",
- "integrity": "sha512-qH/uhaC+AFDU9JfAueSr0epIFJkGMvUPog4FxSEVAtPOur1Oni5WBJMiQIkfHvc7PviVRsnlVLLY2I6221CQew==",
+ "version": "25.4.0",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-25.4.0.tgz",
+ "integrity": "sha512-+0ZrksdaquxGUBwSdTIcdX7VXdwLIlSRsyjivVA9gcO+Cvr6ByqDhu/mi5+HCcb6cMkiQp5xZ8qRO7/eCqLeyw==",
"dev": true,
"requires": {
"bs-logger": "0.x",
"json5": "2.x",
"lodash.memoize": "4.x",
"make-error": "1.x",
+ "micromatch": "4.x",
"mkdirp": "1.x",
"resolve": "1.x",
"semver": "6.x",
- "yargs-parser": "^18.1.1"
+ "yargs-parser": "18.x"
},
"dependencies": {
"camelcase": {
"dev": true
},
"json5": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
- "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
+ }
+ },
+ "micromatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+ "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.0.5"
}
},
"mkdirp": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz",
- "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true
},
"yargs-parser": {
- "version": "18.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.2.tgz",
- "integrity": "sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ==",
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"dev": true
},
"uglify-js": {
- "version": "3.8.1",
- "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz",
- "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==",
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz",
+ "integrity": "sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==",
"dev": true,
"optional": true,
"requires": {
- "commander": "~2.20.3",
- "source-map": "~0.6.1"
- },
- "dependencies": {
- "source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true
- }
+ "commander": "~2.20.3"
}
},
"unc-path-regex": {
}
}
},
+ "untildify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+ "dev": true
+ },
"upath": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-W+1+N/hdzLpQZEcvz79n2IgUE9pfx6JLdHh3Kh8RGvLL8P1LdJVQmi2OsDcLdY4QVID4OUy+FPelyerX0nJxIQ==",
"dev": true
},
- "vlq": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz",
- "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==",
- "dev": true
- },
"vm-browserify": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
"xml-name-validator": "^3.0.0"
}
},
+ "wait-on": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-4.0.0.tgz",
+ "integrity": "sha512-QrW3J8LzS5ADPfD9Rx5S6KJck66xkqyiFKQs9jmUTkIhiEOmkzU7WRZc+MjsnmkrgjitS2xQ4bb13hnlQnKBUQ==",
+ "dev": true,
+ "requires": {
+ "@hapi/joi": "^16.1.8",
+ "lodash": "^4.17.15",
+ "minimist": "^1.2.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.8",
+ "rxjs": "^6.5.4"
+ }
+ },
"walker": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
}
},
"minimist": {
+ "version": "1.2.5",
+ "bundled": true,
"dev": true,
- "optional": true,
- "version": "1.2.5"
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"dev": true,
"optional": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"ms": {
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
"readable-stream": {
"minimalistic-assert": "^1.0.0"
}
},
- "webdriver-js-extender": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
- "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
- "dev": true,
- "requires": {
- "@types/selenium-webdriver": "^3.0.0",
- "selenium-webdriver": "^3.0.1"
- }
- },
"webidl-conversions": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
}
},
"minimist": {
+ "version": "1.2.5",
+ "bundled": true,
"dev": true,
- "optional": true,
- "version": "1.2.5"
+ "optional": true
},
"minipass": {
"version": "2.9.0",
"dev": true,
"optional": true,
"requires": {
- "minimist": "1.2.5"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
+ "minimist": "^1.2.5"
}
},
"ms": {
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
- "minimist": "1.2.5",
+ "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.5"
- }
}
},
"readable-stream": {
}
},
"p-limit": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
- "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"windows-release": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz",
- "integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.0.tgz",
+ "integrity": "sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ==",
"dev": true,
"requires": {
"execa": "^1.0.0"
"dev": true
},
"wordwrap": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
- "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
"dev": true
},
"worker-farm": {
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
"dev": true
},
- "xml2js": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
- "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
- "dev": true,
- "requires": {
- "sax": ">=0.6.0",
- "xmlbuilder": "~11.0.0"
- },
- "dependencies": {
- "sax": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
- "dev": true
- }
- }
- },
- "xmlbuilder": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
- "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
- "dev": true
- },
"xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"camelcase": "^4.1.0"
}
},
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+ "dev": true,
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"test": "npm run test:config && jest --watch",
"test:ci": "npm run test:config && JEST_SILENT_REPORTER_DOTS=true jest --coverage --reporters jest-silent-reporter",
"test:config": "if [ ! -e 'src/unit-test-configuration.ts' ]; then cp 'src/unit-test-configuration.ts.sample' 'src/unit-test-configuration.ts'; fi",
- "e2e": "npm run env_build && npm run e2e:update && ng e2e --webdriverUpdate=false",
- "e2e:ci": "npm run env_build && npm run e2e:update && ng e2e --dev-server-target --webdriverUpdate=false",
- "e2e:update": "npx webdriver-manager update --gecko=false --versions.chrome=$(google-chrome --version | awk '{ print $3 }')",
+ "e2e": "start-test 4200 'cypress open'",
+ "e2e:ci": "start-test 4200 'cypress run -b chrome --headless'",
"lint:tslint": "ng lint",
- "lint:prettier": "prettier --list-different \"{src,e2e}/**/*.{ts,scss}\"",
+ "lint:prettier": "prettier --list-different \"{src,cypress}/**/*.{ts,scss}\"",
"lint:html": "htmllint src/app/**/*.html && html-linter --config html-linter.config.json",
- "lint:tsc": "npm run test:config && tsc -p src/tsconfig.app.json --noEmit && tsc -p tsconfig.spec.json --noEmit && tsc -p e2e/tsconfig.e2e.json --noEmit",
+ "lint:tsc": "npm run test:config && tsc -p src/tsconfig.app.json --noEmit && tsc -p tsconfig.spec.json --noEmit && tsc -p cypress/tsconfig.json --noEmit",
"lint": "npm run lint:tsc && npm run lint:tslint && npm run lint:prettier && npm run lint:html",
- "fix:prettier": "prettier --write \"{src,e2e}/**/*.{ts,scss}\"",
+ "fix:prettier": "prettier --write \"{src,cypress}/**/*.{ts,scss}\"",
"fix:tslint": "npm run lint:tslint -- --fix",
- "fixmod": "pretty-quick --pattern \"{src,e2e}/**/*.{ts,scss}\" --branch HEAD",
+ "fixmod": "pretty-quick --pattern \"{src,cypress}/**/*.{ts,scss}\" --branch HEAD",
"fix": "npm run fix:tslint; npm run fix:prettier",
"fix:audit": "npx npm-force-resolutions",
"compodoc": "compodoc",
"@angular/compiler-cli": "8.2.14",
"@angular/language-service": "8.2.14",
"@compodoc/compodoc": "1.1.11",
- "@types/jasmine": "3.5.10",
- "@types/jasminewd2": "2.0.8",
"@types/jest": "25.1.4",
"@types/lodash": "4.14.149",
"@types/node": "12.12.34",
"@types/simplebar": "5.1.1",
"codelyzer": "5.2.2",
+ "cypress": "4.4.0",
"html-linter": "1.1.1",
"htmllint-cli": "0.0.7",
- "jasmine-core": "3.5.0",
- "jasmine-spec-reporter": "5.0.1",
"jest": "25.2.4",
"jest-canvas-mock": "2.2.0",
"jest-preset-angular": "8.1.3",
"npm-run-all": "4.1.5",
"prettier": "2.0.2",
"pretty-quick": "2.0.1",
- "protractor": "5.4.3",
- "protractor-fail-fast": "3.1.0",
- "protractor-screenshoter-plugin": "0.10.3",
"replace-in-file": "5.0.2",
+ "start-server-and-test": "1.11.0",
"transifex-i18ntool": "1.1.0",
"ts-node": "8.8.1",
"tslint": "6.1.0",
},
"resolutions": {
"mem": "4.3.0",
- "minimist": "1.2.5",
"fsevents": "2.1.2"
}
}
+++ /dev/null
-// Protractor configuration file, see link for more information
-// https://github.com/angular/protractor/blob/master/lib/config.ts
-
-const { SpecReporter } = require('jasmine-spec-reporter');
-let failFast = require('protractor-fail-fast');
-
-const config = {
- SELENIUM_PROMISE_MANAGER: false,
- allScriptsTimeout: 11000,
- implicitWaitTimeout: 9000,
- suites: {
- block: './e2e/block/*.e2e-spec.ts',
- cluster: './e2e/cluster/*.e2e-spec.ts',
- filesystems: './e2e/filesystems/*.e2e-spec.ts',
- nfs: './e2e/nfs/*.e2e-spec.ts',
- pools: './e2e/pools/*.e2e-spec.ts',
- rgw: './e2e/rgw/*.e2e-spec.ts',
- ui: './e2e/ui/*.e2e-spec.ts'
- },
- capabilities: {
- browserName: 'chrome',
- chromeOptions: {
- args: ['--no-sandbox', '--headless', '--window-size=1920x1080']
- },
- acceptInsecureCerts: true
- },
- directConnect: true,
- baseUrl: process.env.BASE_URL || 'http://localhost:4200/',
- framework: 'jasmine',
- jasmineNodeOpts: {
- showColors: true,
- defaultTimeoutInterval: 300000,
- print: function () {}
- },
- params: {
- login: {
- user: process.env.E2E_LOGIN_USER || 'admin',
- password: process.env.E2E_LOGIN_PWD || 'admin'
- }
- },
-
- plugins: [
- {
- package: 'protractor-screenshoter-plugin',
- screenshotPath: '.protractor-report',
- screenshotOnExpect: 'failure',
- screenshotOnSpec: 'none',
- withLogs: true,
- writeReportFreq: 'asap',
- imageToAscii: 'none',
- clearFoldersBeforeTest: true
- },
- failFast.init()
- ],
- afterLaunch: function () {
- failFast.clean();
- }
-};
-
-config.onPrepare = async () => {
- await browser.manage().timeouts().implicitlyWait(config.implicitWaitTimeout);
-
- require('ts-node').register({
- project: 'e2e/tsconfig.e2e.json'
- });
- jasmine
- .getEnv()
- .addReporter(
- new SpecReporter({ spec: { displayStacktrace: 'pretty', displayDuration: true } })
- );
-
- await browser.get('/#/login');
-
- await browser.driver.findElement(by.name('username')).clear();
- await browser.driver.findElement(by.name('username')).sendKeys(browser.params.login.user);
- await browser.driver.findElement(by.name('password')).clear();
- await browser.driver.findElement(by.name('password')).sendKeys(browser.params.login.password);
-
- await browser.driver.findElement(by.css('input[type="submit"]')).click();
-};
-
-exports.config = config;
return lastValueA > lastValueB ? 1 : -1;
}
}
- ],
- data: []
+ ]
};
this.notInQuorum = {
{ prop: 'name', name: this.i18n('Name'), cellTransformation: CellTemplate.routerLink },
{ prop: 'rank', name: this.i18n('Rank') },
{ prop: 'public_addr', name: this.i18n('Public Address') }
- ],
- data: []
+ ]
};
}
@ViewChild('poolConfigurationSourceTpl', { static: false })
poolConfigurationSourceTpl: TemplateRef<any>;
- pools: Pool[] = [];
+ pools: Pool[];
columns: CdTableColumn[];
selection = new CdTableSelection();
modalRef: BsModalRef;
"allowJs": true
},
"exclude": [
- ".protractor-report",
"coverage",
"dist",
- "node_modules"
+ "node_modules",
+ "cypress"
]
}
set -e
+start_ceph() {
+ cd $FULL_PATH_BUILD_DIR
+
+ MGR=2 RGW=1 ../src/vstart.sh -n -d
+ sleep 10
+
+ # Create an Object Gateway User
+ ./bin/radosgw-admin user create --uid=dev --display-name=Developer --system
+ # Set the user-id
+ ./bin/ceph dashboard set-rgw-api-user-id dev
+ # Obtain and set access and secret key for the previously created user. $() is safer than backticks `..`
+ ./bin/ceph dashboard set-rgw-api-access-key $(./bin/radosgw-admin user info --uid=dev | jq -r .keys[0].access_key)
+ ./bin/ceph dashboard set-rgw-api-secret-key $(./bin/radosgw-admin user info --uid=dev | jq -r .keys[0].secret_key)
+ # Set SSL verify to False
+ ./bin/ceph dashboard set-rgw-api-ssl-verify False
+
+ CYPRESS_BASE_URL=$(./bin/ceph mgr services | jq -r .dashboard)
+}
+
stop() {
if [ "$REMOTE" == "false" ]; then
cd ${FULL_PATH_BUILD_DIR}
exit $1
}
-BASE_URL=''
+check_device_available() {
+ : ${DEVICE:="chrome"}
+ failed=false
+
+ if [ "$DEVICE" == "docker" ]; then
+ [ -x "$(command -v docker)" ] || failed=true
+ else
+ cd $DASH_DIR/frontend
+ npx cypress verify
+
+ case "$DEVICE" in
+ chrome)
+ [ -x "$(command -v google-chrome)" ] || [ -x "$(command -v google-chrome-stable)" ] ] || failed=true
+ ;;
+ chromium)
+ [ -x "$(command -v chromium)" ] || failed=true
+ ;;
+ esac
+ fi
+
+ if [ "$failed" = "true" ]; then
+ echo "ERROR: $DEVICE not found. You need to install $DEVICE or \
+ use a different device. Supported devices: chrome (default), chromium, electron or docker."
+ stop 1
+ fi
+}
+
+CYPRESS_BASE_URL=''
DEVICE=''
-E2E_LOGIN_USER=''
-E2E_LOGIN_PWD=''
+CYPRESS_LOGIN_PWD=''
+CYPRESS_LOGIN_USER=''
+NO_COLOR=1
+RECORD=''
REMOTE='false'
while getopts 'd:p:r:u:' flag; do
case "${flag}" in
d) DEVICE=$OPTARG;;
- p) E2E_LOGIN_PWD=$OPTARG;;
+ p) CYPRESS_LOGIN_PWD=$OPTARG;;
r) REMOTE='true'
- BASE_URL=$OPTARG;;
- u) E2E_LOGIN_USER=$OPTARG;;
+ CYPRESS_BASE_URL=$OPTARG;;
+ u) CYPRESS_LOGIN_USER=$OPTARG;;
esac
done
-if [ "$DEVICE" == "" ]; then
- if [ -x "$(command -v google-chrome)" ] || [ -x "$(command -v google-chrome-stable)" ]; then
- DEVICE="chrome"
- elif [ -x "$(command -v docker)" ]; then
- DEVICE="docker"
- else
- echo "ERROR: Chrome and Docker not found. You need to install one of \
-them to run the e2e frontend tests."
- stop 1
- fi
-fi
-
DASH_DIR=`pwd`
-
[ -z "$BUILD_DIR" ] && BUILD_DIR=build
-
cd ../../../../${BUILD_DIR}
FULL_PATH_BUILD_DIR=`pwd`
-if [ "$BASE_URL" == "" ]; then
- MGR=2 RGW=1 ../src/vstart.sh -n -d
- sleep 10
+[[ "$(command -v npm)" == '' ]] && . ${FULL_PATH_BUILD_DIR}/src/pybind/mgr/dashboard/node-env/bin/activate
- # Create an Object Gateway User
- ./bin/radosgw-admin user create --uid=dev --display-name=Developer --system
- # Set the user-id
- ./bin/ceph dashboard set-rgw-api-user-id dev
- # Obtain and set access and secret key for the previously created user. $() is safer than backticks `..`
- ./bin/ceph dashboard set-rgw-api-access-key $(./bin/radosgw-admin user info --uid=dev | jq -r .keys[0].access_key)
- ./bin/ceph dashboard set-rgw-api-secret-key $(./bin/radosgw-admin user info --uid=dev | jq -r .keys[0].secret_key)
- # Set SSL verify to False
- ./bin/ceph dashboard set-rgw-api-ssl-verify False
+: ${CYPRESS_CACHE_FOLDER:="${FULL_PATH_BUILD_DIR}/src/pybind/mgr/dashboard/cypress"}
- BASE_URL=$(./bin/ceph mgr services | jq -r .dashboard)
-fi
+export CYPRESS_BASE_URL CYPRESS_CACHE_FOLDER CYPRESS_LOGIN_USER CYPRESS_LOGIN_PWD NO_COLOR
-export BASE_URL E2E_LOGIN_USER E2E_LOGIN_PWD
+check_device_available
-cd $DASH_DIR/frontend
+if [ "$CYPRESS_BASE_URL" == "" ]; then
+ start_ceph
+fi
-[[ "$(command -v npm)" == '' ]] && . ${FULL_PATH_BUILD_DIR}/src/pybind/mgr/dashboard/node-env/bin/activate
+cd $DASH_DIR/frontend
-if [ "$DEVICE" == "chrome" ]; then
- npm run e2e:ci || stop 1
- stop 0
-elif [ "$DEVICE" == "docker" ]; then
- failed=0
- cat <<EOF > .env
-BASE_URL
-E2E_LOGIN_USER
-E2E_LOGIN_PWD
-EOF
- docker run --rm -v $(pwd):/ceph --env-file .env --name=e2e --network=host --entrypoint "" \
- docker.io/rhcsdashboard/e2e npm run e2e:ci || failed=1
- stop $failed
-else
- echo "ERROR: Device not recognized. Valid devices are 'chrome' and 'docker'."
- stop 1
+if [ -n "$CYPRESS_RECORD_KEY" ]; then
+ RECORD="--record --key $CYPRESS_RECORD_KEY"
fi
+
+case "$DEVICE" in
+ electron)
+ npx cypress run $RECORD || stop 1
+ ;;
+ chrome)
+ npx cypress run $RECORD --browser chrome --headless || stop 1
+ ;;
+ chromium)
+ npx cypress run $RECORD --browser chromium --headless || stop 1
+ ;;
+ docker)
+ failed=0
+ docker run \
+ -v $(pwd):/e2e \
+ -w /e2e \
+ --env CYPRESS_BASE_URL \
+ --env CYPRESS_LOGIN_USER \
+ --env CYPRESS_LOGIN_PWD \
+ --name=e2e \
+ --network=host \
+ cypress/included:4.4.0 || failed=1
+ stop $failed
+ ;;
+esac
+
+stop 0