From fe9c91c47e0e29acee0c2c52a8f6092cb7b4ecd6 Mon Sep 17 00:00:00 2001 From: Aref Shafaei Date: Wed, 11 Sep 2024 18:16:57 -0700 Subject: [PATCH] migrate the remaining specs and remove any notion of protractor --- .github/workflows/e2e.yml | 26 - Makefile | 72 +- README.md | 11 +- docs/dev-docs/e2e-test.md | 8 +- docs/dev-docs/manual-test.md | 4 +- docs/dev-docs/protractor-e2e-test-writing.md | 247 -- docs/dev-docs/protractor-e2e-test.md | 412 --- package-lock.json | 1671 +----------- package.json | 5 +- patches/README.md | 18 - patches/webdriver-manager+12.1.9.patch | 86 - .../parallel-configs/default-config.dev.json | 1 - .../submission-disabled.config.json | 15 - .../recordedit/submission-disabled.dev.json | 9 - .../duplicate_key_conflict.json | 1 - .../submission-disabled-table.json | 1 - .../schema/recordedit/defaults.json | 2 +- .../recordedit/submission-disabled.json | 76 - test/e2e/locators/recordedit.ts | 9 + test/e2e/setup/playwright.model.ts | 10 +- test/e2e/setup/playwright.setup.ts | 11 +- .../errors/errors.spec.js | 114 - .../errors/errors.spec.ts | 56 +- .../specs/default-config/protractor.conf.js | 16 - .../recordedit/immutable-inputs.conf.js | 15 - .../recordedit/immutable-inputs.config.ts | 8 + .../recordedit/immutable-inputs.spec.js | 401 --- .../recordedit/immutable-inputs.spec.ts | 210 ++ .../recordedit/submission-disabled.conf.js | 15 - .../recordedit/submission-disabled.spec.js | 71 - .../navbar/catalog-chaise-config.spec.ts | 1 - .../recordset/histogram-facet.spec.ts | 3 +- test/e2e/utils/chaise.page.js | 1714 ------------ test/e2e/utils/jasmine-matchers.js | 100 - test/e2e/utils/page.utils.js | 238 -- test/e2e/utils/protractor.configuration.js | 165 -- test/e2e/utils/protractor.import.js | 385 --- test/e2e/utils/protractor.parameterize.js | 318 --- test/e2e/utils/record-helpers.js | 1732 ------------ test/e2e/utils/recordedit-helpers.js | 2325 ----------------- test/e2e/utils/recordedit-utils.ts | 6 +- test/e2e/utils/recordset-helpers.js | 146 -- test/manual/specs/recordset.conf.js | 16 - test/manual/specs/recordset.config.ts | 10 + test/manual/specs/recordset.spec.js | 16 - test/manual/specs/recordset.spec.ts | 19 + 46 files changed, 370 insertions(+), 10425 deletions(-) delete mode 100644 docs/dev-docs/protractor-e2e-test-writing.md delete mode 100644 docs/dev-docs/protractor-e2e-test.md delete mode 100644 patches/README.md delete mode 100644 patches/webdriver-manager+12.1.9.patch delete mode 100644 test/e2e/data_setup/config/recordedit/submission-disabled.config.json delete mode 100644 test/e2e/data_setup/config/recordedit/submission-disabled.dev.json delete mode 100644 test/e2e/data_setup/data/submission-disabled/duplicate_key_conflict.json delete mode 100644 test/e2e/data_setup/data/submission-disabled/submission-disabled-table.json delete mode 100644 test/e2e/data_setup/schema/recordedit/submission-disabled.json delete mode 100644 test/e2e/specs/all-features-confirmation/errors/errors.spec.js delete mode 100644 test/e2e/specs/default-config/protractor.conf.js delete mode 100644 test/e2e/specs/default-config/recordedit/immutable-inputs.conf.js create mode 100644 test/e2e/specs/default-config/recordedit/immutable-inputs.config.ts delete mode 100644 test/e2e/specs/default-config/recordedit/immutable-inputs.spec.js create mode 100644 test/e2e/specs/default-config/recordedit/immutable-inputs.spec.ts delete mode 100644 test/e2e/specs/default-config/recordedit/submission-disabled.conf.js delete mode 100644 test/e2e/specs/default-config/recordedit/submission-disabled.spec.js delete mode 100644 test/e2e/utils/chaise.page.js delete mode 100644 test/e2e/utils/jasmine-matchers.js delete mode 100644 test/e2e/utils/page.utils.js delete mode 100644 test/e2e/utils/protractor.configuration.js delete mode 100644 test/e2e/utils/protractor.import.js delete mode 100644 test/e2e/utils/protractor.parameterize.js delete mode 100644 test/e2e/utils/record-helpers.js delete mode 100644 test/e2e/utils/recordedit-helpers.js delete mode 100644 test/e2e/utils/recordset-helpers.js delete mode 100644 test/manual/specs/recordset.conf.js create mode 100644 test/manual/specs/recordset.config.ts delete mode 100644 test/manual/specs/recordset.spec.js create mode 100644 test/manual/specs/recordset.spec.ts diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f79bdb48c..8a5caa2be 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -64,10 +64,6 @@ jobs: sudo pip3 uninstall crypto sudo pip3 uninstall pycrypto sudo pip3 install pycryptodome - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - name: Install webauthn run: | sudo useradd -m -r webauthn @@ -154,25 +150,6 @@ jobs: run: | cd chaise make testdeleteprohibited - - name: Setup and connect to saucelabs - timeout-minutes: 2 - run: | - curl https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz -o saucelabs.tar.gz - tar -xzf saucelabs.tar.gz - cd sc-* - bin/sc -u ${{secrets.SAUCE_USERNAME}} -k ${{secrets.SAUCE_ACCESS_KEY}} -i ${{github.run_id}} -f /tmp/sc_ready --no-ssl-bump-domains=127.0.0.1,localhost --pidfile=/tmp/sc_client.pid & - - name: Check sauce connect - run: | - while [ ! -f /tmp/sc_ready ]; do - echo "Waiting for Sauce Connect..." - sleep 5; - done; - - name: Run default config test spec (protractor) - id: test-protractor-default-config - continue-on-error: true - run: | - cd chaise - make testdefaultconfig-protractor - name: Check on all features test spec if: always() && steps.test-all-features.outcome != 'success' run: exit 1 @@ -185,9 +162,6 @@ jobs: - name: Check on delete prohibited test spec if: always() && steps.test-delete-prohibited.outcome != 'success' run: exit 1 - - name: Check on default config test spec (protractor) - if: always() && steps.test-protractor-default-config.outcome != 'success' - run: exit 1 - uses: actions/upload-artifact@v4 if: always() with: diff --git a/Makefile b/Makefile index 42b1d2ae5..26e6d8451 100644 --- a/Makefile +++ b/Makefile @@ -46,13 +46,12 @@ MODULES=node_modules # Recordedit tests E2EDIrecordAdd=test/e2e/specs/all-features-confirmation/recordedit/add.config.ts E2EDrecordEditNullValues=test/e2e/specs/default-config/recordedit/null-values.config.ts -E2EDIrecordImmutable=test/e2e/specs/default-config/recordedit/immutable-inputs.conf.js +E2EDIrecordImmutable=test/e2e/specs/default-config/recordedit/immutable-inputs.config.ts E2EDIrecordEdit=test/e2e/specs/all-features-confirmation/recordedit/edit-delete.config.ts # not part of the make recordedit command anymore E2EDIrecordMultiFormInput=test/e2e/specs/default-config/multi-form-input/multi-form-input.config.ts E2EDrecordEditCompositeKey=test/e2e/specs/default-config/recordedit/composite-key.config.ts E2EDrecordEditDomainFilter=test/e2e/specs/default-config/recordedit/domain-filter.config.ts -E2EDrecordEditSubmissionDisabled=test/e2e/specs/default-config/recordedit/submission-disabled.conf.js E2ErecordEditForeignKeyDropdown=test/e2e/specs/default-config/recordedit/foreign-key-dropdown.config.ts E2ErecordEditInputIframe=test/e2e/specs/all-features/recordedit/input-iframe.config.ts # Record tests @@ -78,28 +77,19 @@ E2EmultiPermissionsVisibility=test/e2e/specs/all-features/permissions.config.ts E2Efooter=test/e2e/specs/all-features-confirmation/footer/playwright.config.ts # errors test E2Eerrors=test/e2e/specs/all-features-confirmation/errors/errors.config.ts -## Parallel test scripts (protractor) -DefaultConfigParallel_PROTRACTOR=test/e2e/specs/default-config/protractor.conf.js ## Parallel test scripts AllFeaturesParallel=test/e2e/specs/all-features/playwright.config.ts AllFeaturesConfirmationParallel=test/e2e/specs/all-features-confirmation/playwright.config.ts DeleteProhibitedParallel=test/e2e/specs/delete-prohibited/playwright.config.ts DefaultConfigParallel=test/e2e/specs/default-config/playwright.config.ts # Setup for manual tests -Manualrecordset=test/manual/specs/recordset.conf.js - -# protractor tests -RECORDADD_TESTS_PROTRACTOR=$(E2EDIrecordMultiFormInput) $(E2EDIrecordImmutable) -RECORDEDIT_TESTS_PROTRACTOR=$(E2EDrecordEditSubmissionDisabled) -DEFAULT_CONFIG_PARALLEL_TESTS_PROTRACTOR=$(DefaultConfigParallel_PROTRACTOR) -PARALLEL_TESTS_PROTRACTOR=$(DefaultConfigParallel_PROTRACTOR) -ALL_TESTS_PROTRACTOR=$(RECORDSET_TESTS_PROTRACTOR) $(RECORDADD_TESTS_PROTRACTOR) $(RECORDEDIT_TESTS_PROTRACTOR) +Manualrecordset=test/manual/specs/recordset.config.ts # playwright tests NAVBAR_TESTS=$(E2Enavbar) $(E2EnavbarHeadTitle) $(E2EnavbarCatalogConfig) RECORD_TESTS=$(E2EDrecord) $(E2ErecordNoDeleteBtn) $(E2EDrecordRelatedTable) $(E2EDrecordCopy) $(E2EDrecordLinks) RECORDSET_TESTS=$(E2EDrecordset) $(E2ErecordsetAdd) $(E2EDrecordsetEdit) $(E2ErecordsetSavedQuery) $(E2EDrecordsetIndFacet) $(E2EDrecordsetHistFacet) -RECORDADD_TESTS=$(E2EDIrecordAdd) $(E2EDIrecordMultiFormInput) $(E2ErecordEditForeignKeyDropdown) $(E2EDrecordEditCompositeKey) +RECORDADD_TESTS=$(E2EDIrecordAdd) $(E2EDIrecordImmutable) $(E2EDIrecordMultiFormInput) $(E2ErecordEditForeignKeyDropdown) $(E2EDrecordEditCompositeKey) RECORDEDIT_TESTS=$(E2EDIrecordEdit) $(E2EDrecordEditNullValues) $(E2ErecordEditInputIframe) $(E2EDrecordEditDomainFilter) PERMISSIONS_TESTS=$(E2EmultiPermissionsVisibility) FOOTER_TESTS=$(E2Efooter) @@ -113,36 +103,23 @@ ALL_TESTS=$(NAVBAR_TESTS) $(RECORD_TESTS) $(RECORDSET_TESTS) $(RECORDADD_TESTS) ALL_MANUAL_TESTS=$(Manualrecordset) -define make_test_protractor - rc=0; \ - for file in $(1); do \ - npx protractor $$file || rc=1; \ - done; \ - exit $$rc; -endef - +# first argument is the config file location, and second argument will be passed to playwright define make_test rc=0; \ for file in $(1); do \ - npx playwright test --project=chrome --config $$file || rc=1; \ + npx playwright test --project=chrome $(2) --config $$file || rc=1; \ done; \ exit $$rc; endef -test_protractor-%: - $(call make_test_protractor, $($*), "0") - test-%: - $(call make_test, $($*), "0") + $(call make_test, $($*)) #### Sequential make commands - these commands will run tests in sequential order #Rule to run navbar tests .PHONY: testnavbar testnavbar: test-NAVBAR_TESTS -.PHONY: testrecord-protractor -testrecord-protractor: test_protractor-RECORD_TESTS_PROTRACTOR - #Rule to run record tests .PHONY: testrecord testrecord: test-RECORD_TESTS @@ -151,16 +128,10 @@ testrecord: test-RECORD_TESTS .PHONY: testrecordadd testrecordadd: test-RECORDADD_TESTS -.PHONY: testrecordadd-protractor -testrecordadd-protractor: test_protractor-RECORDADD_TESTS_PROTRACTOR - # Rule to run record edit app tests .PHONY: testrecordedit testrecordedit: test-RECORDEDIT_TESTS -.PHONY: testrecordedit-protractor -testrecordedit-protractor: test_protractor-RECORDEDIT_TESTS_PROTRACTOR - # Rule to run permission tests .PHONY: testpermissions testpermissions:test-PERMISSIONS_TESTS @@ -182,9 +153,6 @@ testerrors: test-ERRORS_TESTS .PHONY: testparallel testparallel: test-PARALLEL_TESTS -.PHONY: testparallel-protractor -testparallel-protractor: test_protractor-PARALLEL_TESTS_PROTRACTOR - #Rule to run the All features chaise configuration tests in parallel .PHONY: testallfeatures testallfeatures: test-ALL_FEATURES_PARALLEL_TESTS @@ -201,20 +169,15 @@ testdeleteprohibited: test-DELETE_PROHIBITED_PARALLEL_TESTS .PHONY: testdefaultconfig testdefaultconfig: test-DEFAULT_CONFIG_PARALLEL_TESTS -.PHONY: testdefaultconfig-protractor -testdefaultconfig-protractor: test_protractor-DEFAULT_CONFIG_PARALLEL_TESTS_PROTRACTOR - # Rule to setup schema and data for manual tests .PHONY: testmanually -testmanually: test_protractor-ALL_MANUAL_TESTS +testmanually: + $(call make_test, $(ALL_MANUAL_TESTS), --debug) # Rule to run tests .PHONY: test test: test-ALL_TESTS -.PHONY: test-protractor -test-protractor: test_protractor-ALL_TESTS_PROTRACTOR - # ============================================================= # # BULDING THE PACKAGE # # ============================================================= # @@ -428,14 +391,6 @@ endef # build version will change everytime it's called $(BUILD_VERSION): -# make sure the latest webdriver is installed -# - we fixed the version since this is the latest version that works with -# the protractor version that we're using. -# - we're only using chrome, so we're ignoring gecko installation. -.PHONY: update-webdriver -update-webdriver: - node_modules/protractor/bin/webdriver-manager update --versions.standalone 3.6.0 --gecko false - # install packages (honors NOD_ENV) # using clean-install instead of install to ensure usage of pacakge-lock.json .PHONY: npm-install-modules @@ -443,19 +398,15 @@ npm-install-modules: @npm clean-install # install packages needed for production and development (including testing) -# also run patch-package to patch all the issues in dependencies (currently only webdriver-manager.) -# if we decided to patch other prod dependencies, we should move `patch-package` command to the `postinstall` of package.json. # --include=dev makes sure to ignore NODE_ENV and install everything .PHONY: npm-install-all-modules npm-install-all-modules: @npm clean-install --include=dev - @npx patch-package @npx playwright install --with-deps -# for test cases we have to make sure we're installing dev dependencies and -# webdriver is always updated to the latest version +# for test cases we have to make sure we're installing dev dependencies and playwright is installed .PHONY: deps-test -deps-test: npm-install-all-modules update-webdriver +deps-test: npm-install-all-modules # install all the dependencies .PHONY: deps @@ -574,8 +525,7 @@ usage: @echo " distclean the same as clean, and also removes npm dependencies" @echo " deps local install of node dependencies" @echo " updeps local update of node dependencies" - @echo " update-webdriver update the protractor's webdriver" - @echo " deps-test local install of dev node dependencies and update protractor's webdriver" + @echo " deps-test local install of dev node dependencies and install playwright browsers" @echo " root-install should only be used as root. will use dist with proper user and then deploy, for GNU systems" @echo " root-install-alt should only be used as root. will use dist with proper user and then deploy, for FreeBSD and MAC OS X" @echo " test run e2e tests" diff --git a/README.md b/README.md index 86d278a24..b0c2b5217 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Chaise (Computer-Human Access Interface with Schema Evolution) [![Build Status](https://github.com/informatics-isi-edu/chaise/workflows/Chaise%20end-to-end%20tests/badge.svg?branch=master)](https://github.com/informatics-isi-edu/chaise/actions?query=workflow%3A%22Chaise+end-to-end+tests%22+branch%3Amaster) -A suite of web applications that adapt to the data model for data discovery, analysis, visualization, editing, sharing and collaboration. +A suite of web applications that adapt to the data model for data discovery, analysis, visualization, editing, sharing and collaboration. -Chaise is the main front-end component of the [DERIVA asset management Platform](http://isrd.isi.edu/deriva) and utilizes [ERMrestJS](https://github.com/informatics-isi-edu/ermrestjs) client library to interact with the DERIVA services including [ERMrest](https://github.com/informatics-isi-edu/ermrest) (a general relational data storage service), [webauthn](https://github.com/informatics-isi-edu/webauthn) (authentication provider framework), [Hatrac](https://github.com/informatics-isi-edu/hatrac) (an object store service), and [deriva-web export](https://github.com/informatics-isi-edu/deriva-web/blob/master/docs/export/api.md) (allows export from an ERMrest catalog). +Chaise is the main front-end component of the [DERIVA asset management Platform](http://isrd.isi.edu/deriva) and utilizes [ERMrestJS](https://github.com/informatics-isi-edu/ermrestjs) client library to interact with the DERIVA services including [ERMrest](https://github.com/informatics-isi-edu/ermrest) (a general relational data storage service), [webauthn](https://github.com/informatics-isi-edu/webauthn) (authentication provider framework), [Hatrac](https://github.com/informatics-isi-edu/hatrac) (an object store service), and [deriva-web export](https://github.com/informatics-isi-edu/deriva-web/blob/master/docs/export/api.md) (allows export from an ERMrest catalog). ## Table of Contents @@ -102,7 +102,7 @@ A few representative but non-exhaustive examples of these assumptions include: [data model]: https://en.wikipedia.org/wiki/Data_model [denormalized]: https://en.wikipedia.org/wiki/Denormalization -Beyond these baseline assumptions about basic usage, Chaise makes almost no assumptions about the structure of the underlying [data model], such as its tables, columns, keys, foreign key relationships, etc. Chaise begins by introspecting the data model by getting the `catalog/N/schema` resource from [ERMrest]. The schema resource includes lightweight semantic annotations about the model in addition to the underlying relational database schema. +Beyond these baseline assumptions about basic usage, Chaise makes almost no assumptions about the structure of the underlying [data model], such as its tables, columns, keys, foreign key relationships, etc. Chaise begins by introspecting the data model by getting the `catalog/N/schema` resource from [ERMrest]. The schema resource includes lightweight semantic annotations about the model in addition to the underlying relational database schema. Chaise uses its rending heuristics to decide, for instance, how to flatten a hierarchical structure into a simplified (or [denormalized]) presentation for searching and viewing. The schema annotations are then used to modify or override its rendering heuristics, for instance, to hide a column of a table or to use a specific display name in the interface that is different than the column name from the table definition of the schema. Chaise then applies user preferences to further override the rendering decisions and annotations, for instance, to present a nested table of data in a transposed layout (i.e., with the columns and rows flipped). @@ -116,7 +116,7 @@ The following are some of the documents and resources that we've prepared for us - [Navbar app integration](docs/user-docs/navbar-app.md): Goes over how the Chaise's Navbar app can be embedded in other web applications. - [Logging](docs/user-docs/logging.md): How Chaise is logging server requests as well as client actions. - [Query parameters](docs/user-docs/query-parameters.md): Query parameters that can be used in different Chaise applications. -- [Deveoper guides](docs/user-docs): +- [Deveoper guides](docs/user-docs): - [Chaise developer guide](docs/dev-docs/dev-guide.md): Useful information for developers of Chaise. - [End to End Testing](/docs/dev-docs/e2e-test.md): How end to end testing in Chaise works. @@ -129,7 +129,7 @@ When developing new code for Chaise, please make sure you're following these ste 4. update the e2e tests (if applicable); 5. make sure the liner doesn't throw any errors (`make lint` should not fail); 6. make sure you can deploy your code without any issues (`make dist && make deploy` should not fail); -7. make sure that all tests are passing before submitting the pull request (`make test` should be free of errors); +7. make sure that all tests are passing before submitting the pull request (`make testparallel` should be free of errors); 8. make your pull request, assign it to yourself, and ask someone to review your code. - Try to provide as much information as you can on your PR. Explain the issues that the PR is fixing, and the changes that you've done in the PR. - Provide examples if applicable. @@ -140,6 +140,7 @@ When developing new code for Chaise, please make sure you're following these ste ## Help and Contact Please direct questions and comments to the [project issue tracker](https://github.com/informatics-isi-edu/chaise/issues). + ## License Chaise is made available as open source under the Apache License, Version 2.0. Please see the [LICENSE file](LICENSE) for more information. diff --git a/docs/dev-docs/e2e-test.md b/docs/dev-docs/e2e-test.md index c03b65ce1..4f59865cc 100644 --- a/docs/dev-docs/e2e-test.md +++ b/docs/dev-docs/e2e-test.md @@ -18,7 +18,7 @@ E2E tests are automation tests that simulate a user interacting with the app and - [**Playwright**](https://playwright.dev/): The E2e test framework that we're using. - **NPM**: to install necessary NodeJS packages - **Github workflow**: to do continuous integration (CI) by automatically testing every time code is pushed to Github repo -- **Makefile**: to invoke NPM to install packages necessary for running tests and invoke Protractor (which will run the tests). +- **Makefile**: to invoke NPM to install packages necessary for running tests and invoke Playwright (which will run the tests). ## Setup @@ -64,11 +64,7 @@ You can get your cookie by querying the database, or using the following simple make deps-test ``` - This will install all the npm dependencies that are needed and will also make sure the Selenium's WebDriver that protractor uses is updated. - - - If you just want to update the WebDriver you can do `make update-webdriver`. - - If the version of Chrome that is installed on your machine is different from the ChromeDriver that Selenium uses, it will throw an error. So make sure both versions are always updated and compatible. - + This will install all the npm dependencies that are needed and will also make sure the Playwright browsers are installed. 3. Build Chaise without installing the dependencies again: ```sh diff --git a/docs/dev-docs/manual-test.md b/docs/dev-docs/manual-test.md index 7001b6c1e..35a72a796 100644 --- a/docs/dev-docs/manual-test.md +++ b/docs/dev-docs/manual-test.md @@ -30,7 +30,7 @@ - Run the "make testmanually" command in Chaise. This will set up a recordset table named Accommodation with long tooltips on the last column (Number of Rooms) and the "Operation Since" column. - Manually check the position of the tooltips by hovering over these columns and resizing the window so that these columns are on the window margins. - End the test spec by pressing ^D in the terminal -- The test spec is present at "chaise/test/manual/specs/recordset.spec.js" +- The test spec is present at "chaise/test/manual/specs/recordset.spec.ts" - The tooltip text can be changed at "chaise/test/manual/data_setup/schema/product-recordset.json" ## Recordset - auto truncation of cell content @@ -41,7 +41,7 @@ - Run the "make testmanually" command in Chaise. This will set up a recordset table named Accommodation with long text in 2 columns, "Summary" and "Description" - Maunually check that those 2 columns are truncated and have the "...more" hyperlink. Click that and make sure the column expands to show the full text. Click "...less" and make sure it properly truncates again. - End the test spec by pressing ^D in the terminal -- The test spec is present at "chaise/test/manual/specs/recordset.spec.js" +- The test spec is present at "chaise/test/manual/specs/recordset.spec.ts" - The tooltip text can be changed at "chaise/test/manual/data_setup/schema/product-recordset.json" diff --git a/docs/dev-docs/protractor-e2e-test-writing.md b/docs/dev-docs/protractor-e2e-test-writing.md deleted file mode 100644 index c638eb66b..000000000 --- a/docs/dev-docs/protractor-e2e-test-writing.md +++ /dev/null @@ -1,247 +0,0 @@ - -# Writing Protractor End to End Test Cases - -In this section we have summarized all the resources that you need for writing test cases alongside different issues that we have faced. Please make sure to follow the given instructions. - -## Useful Links - -Protractor uses [Jasmine](http://jasmine.github.io/) for its test syntax. As in unit testing, a test file is comprised of one or more `it` blocks that describe the requirements of your application. `it` blocks are made of *commands* and *expectations*. Commands tell Protractor to do something with the application such as navigate to a page or click on a button. Expectations tell Protractor to assert something about the application's state, such as the value of a field or the current URL. - -Check out the [Angular Docs](https://docs.angularjs.org/guide/e2e-testing) for more on testing your Angular app with Protractor. See the [Protractor docs](https://angular.github.io/protractor/#/tutorial) for more information on Protractor as well. - -There is also an [Angular testing cheat sheet](https://spagettikoodi.wordpress.com/2015/01/14/angular-testing-cheat-sheet/) that will help you get around the syntax for assertions. - -There're tons of features that Protractor supports but here we are going to discuss one which allows for data creation utilities. - -## Test Idioms - -- Try to keep your schema definitions as simple as possible. It only needs to cover the cases that you want to test. Avoid duplicating other existing schemas/tables. -- Don't rely on ERMrestJS heuristics for the parts of the code that you are not testing, and define annotations. The heuristics change more regularly than the annotation won't. For example if you are testing the presentation of record app, define your own visible-columns and visible-foreignkeys annotation. -- Be specific about the scenario that you are testing. If you want to test a very specific scenario, you don't have to test all the other features. For instance, if you want to test recordset page in a specific scenario, you don't have to test all the facet data and main data (The more general case should already be tested and should be separate from this specific test). -- Use names that describe the situation you are trying to recreate. For instance if you are testing the annotations and you want to create a table with annotation 'x' just name the table `table_w_x`. This way we can easily look at the schema and understand which cases are covered in that schema. -- If your test case is related to one of the currently implemented test specs, - - If they can share the same schema, you can modify its schema to cover your case too and add your test case to the corresponding test spec (Instead of creating a new configuration and test spec). - - (More applicable in ERMrestJS)Although it's preferable to not modify other schemas and create your very own schema that covers some specific test cases. -- If you have multiple expect in your `it`, make sure they have their own error message. - -- Separate specific test cases into different `it` functions. - - In E2E tests you might find some test units that are huge, previously they were written that way because of the promise chaining. But you actually can break the chain, and resume again in the next spec. To do so, you should use `done`. Use your judgement on when you should break the test into different `it`s. - ```javascript - // before breaking the chain: - - it ("test", function () { - doTest().then(function () { - // test feature 1 - return doOne(); - }).then(function () { - // test feature 2 that is dependent on feature 1 - return doTwo(); - }).then(function () { - // test feature 3 that is dependent on feature 2 - return doThree(); - }).then(function () { - // test feature 4 - }).catch(function () { - console.log("something bad happened"); - }); - }); - - // the better version: - - it ("test1", function (done) { - doTest().then(function () { - // test feature 1 - return doOne(); - }).then(function () { - // test feature 2 - return doTwo(); - }).then(function () { - done(); - }).catch(function (err) { - done.fail(err); - }); - }); - - it ("test2", function (done) { - doTest().then(function () { - // test feature 3 - return doThree(); - }).then(function () { - // test feature 4 - done(); - }).catch(function (err) { - done.fail(err); - }); - }); - ``` - -## Test FAQ - - -- **element vs. executeScript** - - - The protractor element selector behaves differently from the executeScript jQuery function in one very important way. With the **element** selector, you get the position of that element on the document by it's x,y coordinates. This has caused some issues with elements getting scrolled behind the navbar that are no longer clickable because the click action targets the navbar. With **executeScript**, you are given the element that you selected rather than the position of that element. This allows actions to be performed on that element when it is hidden behind other elements on the page (maybe because of a scroll event). - -- **ignoreSynchronization** - - - The `browser.ignoreSynchronization` option can be set to `true` to force code execution to wait until `$http` requests have returned from the server. Set this option to false to allow code execution to continue while $http requests are being performed. This is useful for testing if buttons are disabled during code execution. - -- **Page being automatically reloaded during test ? I am writing test for recordset in Chaise. But after page is loaded, the address bar is updated with extra “/” after “#”.So the original url `http://…/#1234/schema:table` which becomes `http://…/#/1234/schema:table`, and that causes app to throw error.** - - - This issue has been documented [here](https://github.com/informatics-isi-edu/chaise/issues/582). It happens because of the default angular routing. Protractor depends on it. We are not using the default angular routing in Chaise and Protractor tries to use that when we are changing the location using `browser.get`. For now you just want to get rid of the explicit routing that has been added in your apps. - -- **Test Failed: "defer.fulfill" is not a function** - - - This is mostyl because of a newer version of the promise library [Q](https://www.npmjs.com/package/q). We have fixed the version to `^1.4.1`. If you face issues with any other modules, just try to fix their version numbers in the `package.json` to the last stable version. - -- **Installation issues: saying selenium not found or unable to run tests because of selenium issue or some other module issue** - - - Run `make update-webdriver` and make sure the webdriver has been successfuly installed (it might fail on the first attempt if you're connected to VPN). - -- **Error while clicking a button or finding an element, saying unable to find element/element is not clickable ?** - - - Most of the times when you're trying to look for an element which is rendered as a result of some AJAX or asynchronous operation, you should either wait for it to be rendered or add a delay. Waiting for an element to be visible with a timeout is stable and achievable. This can be done using `browser.wait` and can be seen in action [here](https://github.com/informatics-isi-edu/chaise/blob/master/test/e2e/specs/record/helpers.js#L195) - - -- **Failed: Error while waiting for Protractor to sync with the page: "[ng:test] no injector found for element argument to getTestability.http://errors.angularjs.org/1.5.5/ng/test** - - - You will get this error when you don't explicitly call `browser.get(browser.params.url || "");` in the beforeAll section of your test-case. You should always have a browser.get as Protractor doesn't understands which page should it run the tests against. You can refer a sample [here](https://github.com/informatics-isi-edu/chaise/blob/master/test/e2e/specs/navbar/data-dependent/00-navbar.spec.js#L8) - - -## Page Object Pattern [chaise.page.js](https://github.com/informatics-isi-edu/chaise/blob/master/test/e2e/utils/protractor/chaise.page.js) - -To write the tests, usually one has to first find the DOM elements on the page, then perform some actions on the elements, finally expect (assert) the correct result will show up. There are two logics here, DOM finding and expectation. To separate concerns, most DOM finding logic is put into a **Page Object** file "chaise.page.js". So in ".spec.js" files where the tests (expectations) are written, more effort can be focused on the expectation logic. - -## Expectations - -Expectations should use [Jasmine matchers](https://jasmine.github.io/api/2.6/matchers.html). One thing to note with matchers is that a custom message can be attached to each matcher, `expect(value).toBe(expected_value, failure message)`. This should be done as often as possible to provide a point of contact to look at when debugging errors in test cases. - - -## Expected Conditions - -When writing tests, there will be times where the spec will attempt to run before the elements are available in the DOM. Having tests fail because an element isn't visible has been a sporadic but common enough issue. Protractor has a library called [Expected Conditions](http://www.protractortest.org/#/api?view=ProtractorExpectedConditions) that allows for waiting for a certain part of the DOM to be available before continuing to run tests. - - -## Clicking Elements - -Whenever you want to click an element, make sure that you don't trigger the click function on the element directly. This is because, if the element is not in the sight of browser, it will err out saying unable to find the element and will never get clicked. - -A workaround for this situation is to scroll the page to the element position and then click. `chaise.page.js` has a function to do this. It accepts a WebElement - -```js - -var chaisePage = require('chaise.page.js'); - -var elem = element.all(by.css('.add-button')).first(); - -chaisePage.clickButton(elem); - -``` - -## Synchronization Issues - -Currently Chaise is not using Angular router, thus we can't use Protractor [Synchronization](https://github.com/angular/protractor/blob/9891d430aff477c5feb80ae01b48356866820132/lib/protractor.js#L158). - -Setting it to `true` means that subsequent additions/injections to the the control flow don't also add browser.waitForAngular. - -Synchronization makes protractor wait for Angular promises to resolve and causes the url's to be scrambled. Chaise uses the `#` delimiter to determine url parameters to be sent to Ermrest. Thus, enabling synchronization, changes the url, appending an extra / after the `#` symbol. - -Thus a correct url like this -`https://dev.isrd.isi.edu/chaise/record/#1/legacy:dataset/id=1580` - -changes to - -`https://dev.isrd.isi.edu/chaise/record/#/1/legacy:dataset/id=1580` - -Thus, the parser is unable to parse it. To avoid this we simply disable synchronization in the start of testcases. - -```js -beforeAll(function () { - browser.ignoreSynchronization=true; - browser.get(browser.params.url); -}); -``` - -## Waiting For Elements To Be Visible - -Disabling synchronization has its own issues. Protractor by default waits for the Angular to be done with displaying data or complete http call. With disabled synchronization we need to add logic to handle situations which need the page/element/data to be visible before running the asserts. - -In such cases you might just be tempted to put in a `browser.sleep(2000)` to allow everything to get back on track. - -You shouldn't be using `browser.sleep` functions as they are brittle. Sleeps put your tests at the mercy of changes in your test environments (network speed, machine performance etc). They make your tests slower. From a maintainability point of view they are ambiguous to someone else reading your code. Why were the put there? Is the sleep long enough? They are an acceptance that you don't really know what is going on with your code and in my opinion should be banned (or at least you should try your best to resist the quick win they give you!). - -A simple workaround is to use [browser.wait](http://www.protractortest.org/#/api?view=webdriver.WebDriver.prototype.wait) in conjunction with other parameters such as a timeout/element/variable/functions. - -To wait for elements to be visible/rendered we can simply use following syntax - -```js -chaisePage.waitForElement(element(by.id("some-id"), 5000).then(function() { - console.log("Element found"); -}, function(err) { - console.log("Element not found"); -}); -``` - -## Waiting For Url To Change - -This can be useful when you want to wait for the URL to change in case of form submissions or link clicks. - -```js -var url = "http://dev.isrd.isi.edu/chaise/search"; -chaisePage.waitForUrl(url, 5000).then(function() { - console.log("Redirected to url"); -}, function(err) { - console.log("Unable to redirect to url"); -}); -``` - -### Detecting CI Environment - -There're scenarios where you might need to determine which environment are your tests running; CI or locally. To determine that you can simply refer the variable `process.env.CI`. If it is true then the environment is CI else it is something else. - -```js -if (process.env.CI) { - // DO something CI specific -} else { - // DO something specific to local environment -} -``` - -### Setting Webauthn Cookies in Test Cases - -You can always change the user authentication cookie in your tests. To do so, first you need to have the **webauthn** cookie. - -Now you need to put this code in your test to set the cookie and then redirect the user to specific page on success. The `performLogin(cookie)` function accepts a cookie and returns a promise. On successful completion of login the promise is resolved. - -```js -var chaisePage = require('YOU_PATH/chaise.page.js'); - -describe("Setting Login cookie", function() { - beforeAll(function(done) { - chaisePage.performLogin(COOKIE).then(function() { - // Successfully set the user cookie - // Add your code to redirect the user to another page on beforeAll - }, function(err) { - // Unable to set the cookie - }); - }); -}); - -}); -``` - -You should try to put the performLogin function inside an `it` statement or `beforeAll`/`beforeEach` statement, so that it is executed in the flow of test. - -**NOTE**: Our CI environment doesn't uses HTTPS for CHAISE. When we setup the cookie we set the secure flag in the path for cookie depending on environment. [Reference](http://resources.infosecinstitute.com/securing-cookies-httponly-secure-flags/#gref) - -## Asynchronous actions - -Currently the asynchronous actions are implemented with promise manager of Selenium. -The alternate approach to this can be the use of `async/await`. - -We shouldn't be using both the approaches together as the behaviour is unpredicatable. For adding `async/await` couple of things to keep in mind: -1. Make sure you disabled control flow / promise manager. Mixing awaits with enabled control flow may lead to unpredictable results. -2. Do not forget to prepend ALL your async actions with await (usually this is all protractor api methods). If you will forgot to do this - action without await won't be queued with other actions, so order of actions will be broken. [Source](https://stackoverflow.com/questions/44691940/explain-about-async-await-in-protractor/44701633#44701633) - -**NOTE**: Currently `async/await` is only used for viewport in [deriva-webapps](https://github.com/informatics-isi-edu/deriva-webapps/blob/c7ca3e890c7bf73b23c49ac4cbff5ee2733fa93c/test/e2e/utils/common/deriva-webapps.js#L73). - diff --git a/docs/dev-docs/protractor-e2e-test.md b/docs/dev-docs/protractor-e2e-test.md deleted file mode 100644 index 166b9dae1..000000000 --- a/docs/dev-docs/protractor-e2e-test.md +++ /dev/null @@ -1,412 +0,0 @@ -# End to End Testing With Protractor - -For now, we only have E2E tests in Chaise. E2E tests are automation tests that simulate a user interacting with the app and assert or expect the app would act correctly accordingly. This document will explain how you can configure and run the e2e test cases. Please use [this link](e2e-test-writing.md) to find more information about how to write new test cases. - - -## Tools used -- **Protractor**: E2E framework tailored for AngularJS apps -- **Jasmine**: the way in which the automation tests are written -- **NPM**: to install necessary NodeJS packages -- **SauceLabs**: platform for executing E2E tests and record (in video) the testing results by CI -- **Github workflow**: to do continuous integration (CI) by automatically testing every time code is pushed to Github repo -- **Makefile**: to invoke NPM to install packages necessary for running tests and invoke Protractor (which will run the tests). -- **ErmrestDataUtils**: tool created in house for catalog/schema creation and seeding data ([Found here](https://github.com/informatics-isi-edu/ErmrestDataUtils)). This is installed with the rest of NodeJS packages. - -## Setup - -To run E2E tests on your machine, make sure that you've installed the following on your machine: - -- **Node.js** -- **JAVA** -- **JDK** - -Before running the test cases you also need to set `ERMREST_URL`, `CHAISE_BASE_URL`, `AUTH_COOKIE`, `RESTRICTED_AUTH_COOKIE`, and `REMOTE_CHAISE_DIR_PATH` environment variables. - -```sh -export CHAISE_BASE_URL=YOUR_CHAISE_BASE_URL -export ERMREST_URL=YOUR_ERMREST_URL -export AUTH_COOKIE=YOUR_WEBAUTHN_COOKIE -export RESTRICTED_AUTH_COOKIE=YOUR_SECOND_USER_ERMREST_COOKIE -export REMOTE_CHAISE_DIR_PATH=USERNAME@HOST:public_html/chaise -``` - -These variables are used in our test framework to communicate with `ERMrest`. The following is how these variables most probably should look like: - -```sh -export CHAISE_BASE_URL=https://dev.isrd.isi.edu/~chaise # No trailing `/` -export ERMREST_URL=https://dev.isrd.isi.edu/ermrest # No trailing `/` -export AUTH_COOKIE="webauthn=PutYourCookieHere;" # You have to put `webauthn=` at the beginging and `;` at the end. -export RESTRICTED_AUTH_COOKIE="webauthn=PutAnotherCookieHere;" # You have to put `webauthn=` at the beginging and `;` at the end. -export REMOTE_CHAISE_DIR_PATH=chirag@dev.isrd.isi.edu:public_html/chaise # No trailing `/` -export SHARDING=false -``` - -You can get your cookie by querying the database, or using the following simple steps: - -1. Open up any chaise page in the deployment that you want to run test cases on. -2. Login. The account that you are using must have delete and create access. We use this cookie to create and delete catalogs. -3. Open the Developer tools in your browser. -4. Go to the console section and write `$.cookie("webauthn")`. - - -## How To Run Tests -### Prerequistes -1. After setting up the environment variables, make sure that the `https://dev.isrd.isi.edu/~` directory has the public access(if not, give the folder the following permissions `chmod 755 `). - -2. Make sure all the dependencies are installed by running the following command: - - ```sh - make deps-test - ``` - - This will install all the npm dependencies that are needed and will also make sure the Selenium's WebDriver that protractor uses is updated. - - - If you just want to update the WebDriver you can do `make update-webdriver`. - - If the version of Chrome that is installed on your machine is different from the ChromeDriver that Selenium uses, it will throw an error. So make sure both versions are always updated and compatible. - - -3. Build Chaise without installing the dependencies again: - ```sh - make dist-wo-deps - ``` - As the name suggests this will not install dependencies. That's why you need to install all the dependencies in step 2. - -4. Upload your code on the `https://dev.isrd.isi.edu/~` by the running the following command in your local chaise repository (This will upload your local code to the remote server): - - ```sh - make deploy - ``` - If you want to also deploy the existing config files in your local machine, you can use the `make deploy-w-config` command instead. - - -### Test cases -- To execute all test cases in sequential order, set the following: - ```sh - export SHARDING=false - ``` - - and then run the following command: - - ```sh - $ make test - ``` - -- To execute all the test cases in parallel, set the following: - - ```sh - export SHARDING=true - ``` - - and then run the following command: - - ```sh - $ make testparallel - ``` - -- To run a specific test spec - - ```sh - $ node_modules/.bin/protractor test/e2e/specs/search/data-independent/protractor.conf.js - ``` - -## File structure - -``` -chaise/ -`-- test/e2e/ - |-- data_setup - | |-- config # test configuration files - | | |-- record - | | | |-- *.dev.json # lists the schema config files (*.config.json) - | | | `-- *.config.json # the configuration for data utils that connects the schema and data together - | | `-- recordedit - | |-- data # Data - | | `-- SCHEMA_NAME - | | |-- TABLE_NAME.json - | | `-- TABLE_NAME.json # Table entities json - | `-- schema - | |-- record - | | `-- SCHEMA_NAME.json - | `-- SCHEMA_NAME.json # Schema Definition json - |-- specs - | `-- all-features - | |-- record - | | |-- TEST_NAME1.spec.js - | | `-- TEST_NAME1.conf.js # protractor configuration for similarly named single test - | |-- recordedit - | | # They introspect the existing schema to run the cases*/ - | `-- protractor.conf.js # configuration for the parallel tests - `-- utils - |-- chaise.page.js # To declare Page Objects, google "page object pattern" for details - |-- page.utils.js # Utilities for Page objects - `-- protractor.configuration.js # Scaffolding for all configurations - -``` - -## Configuration -**Protractor.configuration.js**: the protractor.configuration.js file is a scaffolding for all tests and specifies which browser to use when executing tests using -```sh -capabilities: { - //browserName: 'internet explorer', - //browserName: 'firefox', - //version: '40.0', //to specify the browser version - browserName: 'chrome', - }, -``` -It specifies tests to be run on multiple browsers, -```sh -multiCapabilities: [{ - 'browserName': 'firefox' - }, { - 'browserName': 'chrome' - }], -``` - -> While protractor can run in multiple browsers, we're only using Chrome for our e2e tests. To test Firefox, we would have to: -> - Change the `make update-webdriver` command to also install gecko drivers. -> - Change the configuration to properly set the browser settings in Firefox the same as Chrome (window size, etc.) -> - Make sure test specs are working properly on Firefox as well. - - -To specify which test specs to run, use -```sh -specs: [ - '*.spec.js' - ], -``` - -To configure SauceLabs credentials, -```sh -sauceUser: process.env.SAUCE_USERNAME, -sauceKey: process.env.SAUCE_ACCESS_KEY, -``` - -### protractor.conf.js - -You must refer this scaffolding in your `protractor.conf.js` file and pass the parameters to avoid duplicating it. This is how your file would look like. - -```javascript -var pConfig = require('./../../../utils/protractor.configuration.js'); - -var config = pConfig.getConfig({ - - // Change this to your desired filed name and Comment below testConfiguration object declaration - configFileName: 'search.dev.json', - - /* Just in case if you plan on not giving a file for configuration, you can always specify a testConfiguration object - * Comment above 2 lines - * Empty configuration will run test cases against catalog 1 and default schema - testConfiguration: { - .... - }, - */ - - // Optional: Default is "chaise-config.js" - chaiseConfigFilePath: "path/to/your/chaise-config/to/be.used/in/this/test/suite", - - // Full path from the base of base directory - //chaiseConfigFilePath: "test/e2e/specs/recordedit/data-independent/edit/chaise-config.js" - - - /* If you want to set a custom base URL with some query string or other url parameters use - * setBaseUrl function else directly set the page. You just need to provide wither of them. - */ - page: '/search', - - /* - setBaseUrl: function(browser, data) { - browser.params.url = process.env.CHAISE_BASE_URL + "/search" + "/#" + data.catalogId + "/" + data.schema.name; - return browser.params.url; - } - */ -}); - -exports.config = config; -``` -#### ChaiseConfigFilePath - -When a tester specifies a chaiseConfigFilePath, it copies the file specified in the `chaiseConfigFilePath` into REMOTE_CHAISE_DIR_PATH/chaise-config.js. This functionality is to make it work locally on developer machine pointing to some other server. In addition, to avoid the newly copied `chaise-config.js` file being used for other suites, it is reverted back to the previous one. Here previous refers to the `chaise-config-sample.js file`. - -**NOTE**: To specify which config you want to use for your spec, set `chaiseConfigFilePath` in your **protractor.conf.js** file as follows. You also need to run the [ssh-agent](http://mah.everybody.org/docs/ssh) in background as well as export a new environment variable that specifies your remote **dev.isrd** sshpath. - -For example -```sh -# run ssh-agent in background -$ eval ssh-agent - -# add your key to ssh-agent -$ ssh-add PATH/TO/KEY - -# export REMOTE_CHAISE_DIR_PATH=USERNAME@HOST:public_html/chaise -$ export REMOTE_CHAISE_DIR_PATH=chirag@dev.isrd.isi.edu:public_html/chaise -``` - -**CI**: For CI there is no need to set `REMOTE_CHAISE_DIR_PATH` as it copies the actual file to the **chaise-config.js** in its local directory where it is running the test-suite. - -### Test Configuration JSON file - -The `configFileName` which is **"search.dev.json"** here, specifies the file where the JSON for this suite exists. Apart from that everything is self-explanatory in the JSON. - -The structure of the configuration is -```javascript -{ - "setup": { - "schemaConfigurations" : [{ - "catalog": { - //"id": 1 //existing id of a catalog - }, - "schema": { - "name": "product", - "createNew": true, // change this to false to avoid creating new schema - "path": "./schema/product.json" // path of the schema json file in data_setup folder - }, - "tables": { - "createNew": true, // Mention this to be true to allow creating new tables - }, - "entities": { - "createNew": true, // Mention this to be true to allow creating new entities - "path": "./data/product", // This is the path from where the json for the entities will be picked for import - }, - "authCookie": "" - }], - "schema": "DEFAULT_NAME_OF_SCHEMA" // (Optional: Will set the default schema to the name you provide) - }, - "cleanup": true, //Do you want to delete the created catalog/schema/tables/entities created in the setup phase - "tests": { - - } -} -``` - -The `setup` object allows you to specify whether the test-cases should create or use existing catalog, schema, tables and entities before running the testcases. -The `cleanup` property if true will delete all the data that was created in the dataSetup phase. -The `tests` object is mandatory for data-dependent testcases, as the test-cases will refer to this configuration for running and asserting them. - -```javascript - "tests" : { - "Filters on top of the records," : { // This is the name of describe which will used this data - - // The internal data doesn't has a format, its just an object which can change accorsing to your test needs/ - "data" : "testcase2Input2", - "someMoreData" : "data" - . - . - . - . - } - } -``` - -## Test Parallelization - -By default the e2e tests will run in parallel. To avoid this, set the `Sharding` environment variable to `false`. - -```export SHARDING=false``` - -To run a set of specs in parallel you can set some flags in the `protractor.conf.js`. - -```js - -{ - .. - "parallel": 3, //No of instances or just set it to true for default (4) - .. -} - -``` - -Internally the `parallel` property gets resolved to number of instances and enabling/disabling sharding. - -```js -if (options.parallel) { - config.capabilities.shardTestFiles = true; - config.capabilities.maxInstances = typeof options.parallel == 'number' ? options.parallel : 4; -} -``` - -You can see the code [here](https://github.com/informatics-isi-edu/chaise/blob/master/test/e2e/utils/protractor.configuration.js) - -## Running tests using SAUCE LABS locally - -To run tests locally using [Sauce Labs](https://saucelabs.com) you need to have an account with them. Once you signup you will need to extract your `username` and `access key`. - -You can access them from the `My account` link in user dropdown. On the `My account` page you will find your username and access key. After which you need to set them in your environment. - -```sh -export SAUCE_USERNAME="your_username" -export SAUCE_ACCESS_KEY="your_access_key" -``` - -Now when you run the tests, they will be executed on Sauce Labs instead of your machine. You can monitor them on their dashboard. - -## Running tests in CI - -### CI Testing workflow -in the Chaise folder, invoke command -```sh -npm test -``` -The "test" command specified in package.json (NodeJS conf file) will be executed, so command -```sh -make test -``` -will be executed. Command specified in Makefile will then be executed, which will invoke Protractor to execute the tests. -```sh -protractor search/data-independent/protractor.conf.js && search/data-dependent/protractor.conf.js && protractor recordedit/data-independentadd/protractor.conf.js && protractor recordedit/data-independentadd/protractor.conf.js -``` -Protractor will run "protractor search/data-independent/protractor.conf.js" first, if there is no failure in search/data-independent/protractor.conf.js specs, protractor will proceed to execute "protractor search/data-dependent/protractor.conf.js". If there are failures in search/data-dependent/protractor.conf.js specs, the tests will exit. And it keeps going on until it has executed all protractor test suites. - -Usually Protractor will open a local server (and browsers) to execute the e2e tests. But because we want the tests to be executed on SauceLabs, lines below are added to protractor conf files. -```sh -sauceUser: process.env.SAUCE_USERNAME, -sauceKey: process.env.SAUCE_ACCESS_KEY, - -``` -These lines will set up credentials necessary to login SauceLabs and when detected, will execute these tests on SauceLabs. SauceLabs will then print the testing result on terminal and record the testing video on that account as well. When tests are finished, there will be a URL printed on terminal directing you to see the recorded results. - -When the code is pushed to ISI repo, the "Chaise end-to-end tests" Github workflow will run appropriate make commands, which will trigger the testing chain specified above. - -### Environment Variables - -**./.github/workflows/main.yml**: [.e2e.yml](https://github.com/informatics-isi-edu/chaise/blob/master/.github/workflows/e2e.yml) -specifies environment variables in Github workflow so that tests can be run successfully. Usually the env variables are set using terminal locally, to set them up in CI environment one has to configure e2e.yml file. - -- Since CI e2e test require connecting to saucelabs, `SAUCE_USERNAME`, `SAUCE_ACCESS_KEY`, - and `SAUCE_TUNNEL_IDENTIFIER` are defined . `SAUCE_TUNNEL_IDENTIFIER` is used - internally in the `e2e.yml` to create a saucelab tunnel with that identifier. And then - during the setup the same identifier is used to ensure connecting to the correct tunnel. -- `SHARDING: true` ensures running test cases in parallel. - -While running tests on **CI** you don't need to set the `REMOTE_CHAISE_DIR_PATH` in **main.yml** file. - -## Debugging - -You can use [the Node.js built-in inspector](https://nodejs.org/en/docs/inspector) to debug the test cases. To do so, - -1. Find the configuration that you want to debug. Since we're going to use Node.js, we have to directly target the configuration and cannot use the existing `make` targets. To make this easier, you can find the config locations in `Makefile`. In this example, we want to only debug faceting tests which is, - ``` - test/e2e/specs/delete-prohibited/recordset/ind-facet.conf.js - ``` - -2. Run protractor using `--inspect-break` to ensure debugging client waits for you to open it: - ``` - node --inspect-break ./node_modules/.bin/protractor test/e2e/specs/delete-prohibited/recordset/ind-facet.conf.js - ``` - - By default this will use the `9229` port. You can change this by doing `--inspect-break=0.0.0.0:1234`. - -3. The previous command will create a debugger that listens to a specific port, and then waits for you to open an [inspector client](https://nodejs.org/en/docs/guides/debugging-getting-started/#inspector-clients). For the purpose of this document, we're going to use Chrome DevTools. So open Chrome and navigate to the following location: - ``` - chrome://inspect - ``` - -4. In the Chrome's inspect page, under the "Remote Target", you should see your target. Click on "inspect" for that target. - -5. Chrome will open a new window that is focused on "Sources" tab. If this is the first time that you're debugging, you need to add Chaise folder to Chrome's workspace. To do so just click on "Add folder to workspace" and choose Chaise folder. - -6. Now you can go ahead and find the file that you want to debug in Chaise folder and add your break points to the test folder. - -7. Resume the execution after you've added your breakpoints and wait for protractor to reach that part of code. - -## Writing test - -Please use [this link](e2e-test-writing.md) to find more information about how to write new test cases. diff --git a/package-lock.json b/package-lock.json index 465f2e4d9..ccb0a16b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,9 +61,7 @@ "jasmine-spec-reporter": "^2.5.0", "moment": "2.29.4", "moment-timezone": "0.5.14", - "patch-package": "^8.0.0", "prettier": "^3.2.5", - "protractor": "5.4.4", "set-cookie-parser": "x", "ts-node": "^10.9.1", "webpack-bundle-analyzer": "^4.5.0" @@ -2586,11 +2584,6 @@ "@types/react": "*" } }, - "node_modules/@types/selenium-webdriver": { - "version": "3.0.19", - "dev": true, - "license": "MIT" - }, "node_modules/@types/sizzle": { "version": "2.3.3", "license": "MIT" @@ -3012,11 +3005,6 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/abs-svg-path": { "version": "0.1.1", "license": "MIT", @@ -3058,25 +3046,6 @@ "node": ">=0.4.0" } }, - "node_modules/adm-zip": { - "version": "0.5.10", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/agent-base": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/ajv": { "version": "6.12.6", "license": "MIT", @@ -3136,14 +3105,6 @@ "license": "MIT", "peer": true }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -3263,14 +3224,6 @@ "node": ">=8" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -3385,30 +3338,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/arrify": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3419,14 +3348,6 @@ "version": "0.4.0", "license": "MIT" }, - "node_modules/at-least-node": { - "version": "1.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3442,19 +3363,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "dev": true, - "license": "MIT" - }, "node_modules/axe-core": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", @@ -3560,14 +3468,6 @@ "node": ">= 0.6.0" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/big.js": { "version": "5.2.2", "license": "MIT", @@ -3606,20 +3506,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/blocking-proxy": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "blocking-proxy": "built/lib/bin.js" - }, - "engines": { - "node": ">=6.9.x" - } - }, "node_modules/boolbase": { "version": "1.0.0", "license": "ISC" @@ -3686,14 +3572,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/browserstack": { - "version": "1.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, "node_modules/buffer-from": { "version": "1.1.2", "license": "MIT" @@ -3734,14 +3612,6 @@ "tslib": "^2.0.3" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001612", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", @@ -3769,11 +3639,6 @@ "element-size": "^1.1.1" } }, - "node_modules/caseless": { - "version": "0.12.0", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -3874,35 +3739,6 @@ "node": ">=0.10.0" } }, - "node_modules/cliui": { - "version": "4.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "license": "MIT", @@ -3915,14 +3751,6 @@ "node": ">=6" } }, - "node_modules/code-point-at": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/color-alpha": { "version": "1.0.4", "license": "MIT", @@ -4062,7 +3890,8 @@ }, "node_modules/core-util-is": { "version": "1.0.3", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/country-regex": { "version": "1.1.0", @@ -4378,17 +4207,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/dashdash": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -4455,14 +4273,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -4544,61 +4354,6 @@ "license": "MIT", "peer": true }, - "node_modules/del": { - "version": "2.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/array-union": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/globby": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "license": "MIT", @@ -4764,15 +4519,6 @@ "license": "ISC", "peer": true }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/electron-to-chromium": { "version": "1.4.746", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.746.tgz", @@ -4806,6 +4552,7 @@ "node_modules/end-of-stream": { "version": "1.4.4", "license": "MIT", + "peer": true, "dependencies": { "once": "^1.4.0" } @@ -5052,19 +4799,6 @@ "es6-symbol": "^3.1.1" } }, - "node_modules/es6-promise": { - "version": "4.2.8", - "dev": true, - "license": "MIT" - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es6-promise": "^4.0.3" - } - }, "node_modules/es6-symbol": { "version": "3.1.3", "license": "ISC", @@ -5849,13 +5583,6 @@ "node": "*" } }, - "node_modules/exit": { - "version": "0.1.2", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ext": { "version": "1.6.0", "license": "ISC", @@ -5869,19 +5596,6 @@ "license": "ISC", "peer": true }, - "node_modules/extend": { - "version": "3.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, "node_modules/falafel": { "version": "2.2.5", "license": "MIT", @@ -6099,14 +5813,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-yarn-workspace-root": { - "version": "2.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "micromatch": "^4.0.2" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -6178,27 +5884,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/from2": { "version": "2.3.0", "license": "MIT", @@ -6208,20 +5893,6 @@ "readable-stream": "^2.0.0" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, @@ -6284,11 +5955,6 @@ "license": "ISC", "peer": true }, - "node_modules/get-caller-file": { - "version": "1.0.3", - "dev": true, - "license": "ISC" - }, "node_modules/get-canvas-context": { "version": "1.0.2", "license": "MIT", @@ -6340,14 +6006,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getpass": { - "version": "0.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/gl-mat4": { "version": "1.2.0", "license": "Zlib", @@ -6696,37 +6354,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "dev": true, @@ -6896,40 +6523,6 @@ "entities": "^2.0.0" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "2.2.4", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/human-signals": { "version": "2.1.0", "license": "Apache-2.0", @@ -6994,11 +6587,6 @@ "npm": ">=4.0.0" } }, - "node_modules/immediate": { - "version": "3.0.6", - "dev": true, - "license": "MIT" - }, "node_modules/immutable": { "version": "4.0.0", "license": "MIT" @@ -7057,11 +6645,6 @@ "version": "2.0.4", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "dev": true, - "license": "ISC" - }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -7090,14 +6673,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/invert-kv": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -7240,20 +6815,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "license": "MIT", @@ -7292,14 +6853,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -7392,36 +6945,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-cwd": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-in-cwd": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "license": "MIT", @@ -7546,11 +7069,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -7590,20 +7108,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "1.0.0", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/isexe": { "version": "2.0.0", @@ -7616,11 +7124,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "dev": true, - "license": "MIT" - }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -7634,24 +7137,6 @@ "set-function-name": "^2.0.1" } }, - "node_modules/jasmine": { - "version": "2.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "2.8.0", - "dev": true, - "license": "MIT" - }, "node_modules/jasmine-spec-reporter": { "version": "2.7.0", "dev": true, @@ -7660,14 +7145,6 @@ "colors": "1.1.2" } }, - "node_modules/jasminewd2": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6.9.x" - } - }, "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", @@ -7806,11 +7283,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "dev": true, - "license": "MIT" - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7832,36 +7304,15 @@ "version": "2.3.1", "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "license": "MIT" }, - "node_modules/json-stable-stringify": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "jsonify": "^0.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/json5": { "version": "2.2.3", "license": "MIT", @@ -7872,39 +7323,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonify": { - "version": "0.0.1", - "dev": true, - "license": "Public Domain", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jsprim": { - "version": "1.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -7920,17 +7338,6 @@ "node": ">=4.0" } }, - "node_modules/jszip": { - "version": "3.9.1", - "dev": true, - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, "node_modules/kdbush": { "version": "3.0.0", "license": "ISC", @@ -7952,14 +7359,6 @@ "node": ">=0.10.0" } }, - "node_modules/klaw-sync": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.11" - } - }, "node_modules/klona": { "version": "2.0.5", "license": "MIT", @@ -7985,17 +7384,6 @@ "node": ">=0.10" } }, - "node_modules/lcid": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "invert-kv": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8009,14 +7397,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/loader-runner": { "version": "4.3.0", "license": "MIT", @@ -8082,17 +7462,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/map-limit": { "version": "0.0.1", "license": "MIT", @@ -8149,19 +7518,6 @@ "node": ">=0.10.0" } }, - "node_modules/mem": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/memoize-one": { "version": "5.2.1", "license": "MIT" @@ -8378,11 +7734,6 @@ "license": "ISC", "peer": true }, - "node_modules/nice-try": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, "node_modules/no-case": { "version": "3.0.4", "license": "MIT", @@ -8439,22 +7790,6 @@ "node": ">=0.10.0" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "license": "MIT", @@ -8599,21 +7934,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "7.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/opener": { "version": "1.5.2", "dev": true, @@ -8639,164 +7959,6 @@ "node": ">= 0.8.0" } }, - "node_modules/os-locale": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/os-locale/node_modules/cross-spawn": { - "version": "6.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/os-locale/node_modules/execa": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/os-locale/node_modules/get-stream": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/os-locale/node_modules/is-stream": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/npm-run-path": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/path-key": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/os-locale/node_modules/semver": { - "version": "5.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/os-locale/node_modules/shebang-command": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/shebang-regex": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale/node_modules/which": { - "version": "1.3.1", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-is-promise": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" - }, "node_modules/param-case": { "version": "3.0.4", "license": "MIT", @@ -8848,151 +8010,6 @@ "tslib": "^2.0.3" } }, - "node_modules/patch-package": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@yarnpkg/lockfile": "^1.1.0", - "chalk": "^4.1.2", - "ci-info": "^3.7.0", - "cross-spawn": "^7.0.3", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^9.0.0", - "json-stable-stringify": "^1.0.2", - "klaw-sync": "^6.0.0", - "minimist": "^1.2.6", - "open": "^7.4.2", - "rimraf": "^2.6.3", - "semver": "^7.5.3", - "slash": "^2.0.0", - "tmp": "^0.0.33", - "yaml": "^2.2.2" - }, - "bin": { - "patch-package": "index.js" - }, - "engines": { - "node": ">=14", - "npm": ">5" - } - }, - "node_modules/patch-package/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/patch-package/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/patch-package/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/patch-package/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/patch-package/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/patch-package/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/patch-package/node_modules/slash": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/patch-package/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/patch-package/node_modules/tmp": { - "version": "0.0.33", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "dev": true, @@ -9001,11 +8018,6 @@ "node": ">=0.10.0" } }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "dev": true, - "license": "(WTFPL OR MIT)" - }, "node_modules/path-key": { "version": "3.1.1", "license": "MIT", @@ -9040,7 +8052,8 @@ }, "node_modules/performance-now": { "version": "2.1.0", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/pick-by-alias": { "version": "1.2.0", @@ -9061,33 +8074,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "license": "MIT", @@ -9408,7 +8394,8 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/prop-types": { "version": "15.8.1", @@ -9435,109 +8422,10 @@ "license": "MIT", "peer": true }, - "node_modules/protractor": { - "version": "5.4.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@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", - "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", - "yargs": "^12.0.5" - }, - "bin": { - "protractor": "bin/protractor", - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/protractor/node_modules/@types/q": { - "version": "0.0.32", - "dev": true, - "license": "MIT" - }, - "node_modules/protractor/node_modules/ansi-styles": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/chalk": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/q": { - "version": "1.4.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/protractor/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/supports-color": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "license": "MIT" }, - "node_modules/psl": { - "version": "1.8.0", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.1.1", "license": "MIT", @@ -9553,14 +8441,6 @@ "teleport": ">=0.2.0" } }, - "node_modules/qs": { - "version": "6.5.3", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9786,6 +8666,7 @@ "node_modules/readable-stream": { "version": "2.3.7", "license": "MIT", + "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -10019,44 +8900,6 @@ "strip-ansi": "^6.0.1" } }, - "node_modules/request": { - "version": "2.88.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "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" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "license": "MIT", @@ -10064,11 +8907,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "1.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -10225,7 +9063,8 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/sass": { "version": "1.58.3", @@ -10278,19 +9117,10 @@ } } }, - "node_modules/saucelabs": { - "version": "1.5.0", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/sax": { "version": "1.2.4", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/scheduler": { "version": "0.23.2", @@ -10349,31 +9179,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/selenium-webdriver": { - "version": "3.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/selenium-webdriver/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/semver": { "version": "6.3.1", "license": "ISC", @@ -10389,11 +9194,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/set-cookie-parser": { "version": "2.4.8", "dev": true, @@ -10431,14 +9231,6 @@ "node": ">= 0.4" } }, - "node_modules/set-immediate-shim": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/shallow-clone": { "version": "3.0.1", "license": "MIT", @@ -10520,14 +9312,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.5.7", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.0.2", "license": "BSD-3-Clause", @@ -10535,42 +9319,10 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.4.18", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "^0.5.6" - } - }, "node_modules/spark-md5": { "version": "3.0.2", "license": "(WTFPL OR MIT)" }, - "node_modules/sshpk": { - "version": "1.17.0", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stack-trace": { "version": "0.0.9", "peer": true, @@ -10624,50 +9376,20 @@ "license": "MIT", "peer": true }, - "node_modules/string_decoder": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string-split-by": { - "version": "1.0.0", - "license": "MIT", - "peer": true, - "dependencies": { - "parenthesis": "^3.1.5" - } - }, - "node_modules/string-width": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "3.0.1", - "dev": true, + "node_modules/string_decoder": { + "version": "1.1.1", "license": "MIT", - "engines": { - "node": ">=4" + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "4.0.0", - "dev": true, + "node_modules/string-split-by": { + "version": "1.0.0", "license": "MIT", + "peer": true, "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" + "parenthesis": "^3.1.5" } }, "node_modules/string.prototype.includes": { @@ -10791,14 +9513,6 @@ "node": ">=4" } }, - "node_modules/strip-eof": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "license": "MIT", @@ -11044,17 +9758,6 @@ "license": "ISC", "peer": true }, - "node_modules/tmp": { - "version": "0.0.30", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "license": "MIT", @@ -11112,18 +9815,6 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -11207,22 +9898,6 @@ "version": "2.4.0", "license": "0BSD" }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "dev": true, - "license": "Unlicense" - }, "node_modules/type": { "version": "1.2.0", "license": "ISC", @@ -11433,14 +10108,6 @@ "node": ">=4" } }, - "node_modules/universalify": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unquote": { "version": "1.1.1", "license": "MIT", @@ -11502,38 +10169,12 @@ "version": "0.4.0", "license": "MIT" }, - "node_modules/uuid": { - "version": "3.4.0", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "node_modules/verror": { - "version": "1.10.0", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/vt-pbf": { "version": "3.1.3", "license": "MIT", @@ -11568,103 +10209,6 @@ "license": "Apache-2.0", "peer": true }, - "node_modules/webdriver-js-extender": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager": { - "version": "12.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "adm-zip": "^0.5.2", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "bin": { - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-styles": { - "version": "2.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/chalk": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "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" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/rimraf": { - "version": "2.7.1", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/webdriver-manager/node_modules/semver": { - "version": "5.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/webdriver-manager/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/supports-color": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/webgl-context": { "version": "2.2.0", "license": "MIT", @@ -11973,11 +10517,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-module": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, "node_modules/which-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", @@ -12017,53 +10556,6 @@ "object-assign": "^4.1.0" } }, - "node_modules/wrap-ansi": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "license": "ISC" @@ -12089,26 +10581,6 @@ } } }, - "node_modules/xml2js": { - "version": "0.4.23", - "dev": true, - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, "node_modules/xtend": { "version": "4.0.2", "license": "MIT", @@ -12117,107 +10589,10 @@ "node": ">=0.4" } }, - "node_modules/y18n": { - "version": "4.0.3", - "dev": true, - "license": "ISC" - }, "node_modules/yallist": { "version": "4.0.0", "license": "ISC" }, - "node_modules/yaml": { - "version": "2.3.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "12.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "node_modules/yargs-parser": { - "version": "11.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/locate-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 7d6b18a21..c34ea72dd 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,9 @@ ], "scripts": { "build": "make dist", + "deploy": "make deploy", "pretest": "make deploy", - "test": "make test" + "test": "make testparallel" }, "repository": { "type": "git", @@ -42,9 +43,7 @@ "jasmine-spec-reporter": "^2.5.0", "moment": "2.29.4", "moment-timezone": "0.5.14", - "patch-package": "^8.0.0", "prettier": "^3.2.5", - "protractor": "5.4.4", "set-cookie-parser": "x", "ts-node": "^10.9.1", "webpack-bundle-analyzer": "^4.5.0" diff --git a/patches/README.md b/patches/README.md deleted file mode 100644 index fe57426a7..000000000 --- a/patches/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Dependency patches - -This folder includes the quick fixes we must do to our dependencies. This should only be used as a quick fix or in cases where a dependency is deprecated or won't fix the feature we need. The following are the changes that we have made to our dependencies: - -## webdriver-manager - -Used internally by `protractor` to create a selenium server. This package installs the latest ChromeDriver compatible with the installed Chrome version. But since version 115, the location of ChromeDriver has changed ([link](https://chromedriver.chromium.org/downloads/version-selection)). But the `webdriver-manager@12.1.9` still points to the old location. While based on [this issue](https://github.com/angular/webdriver-manager/issues/524) they are planning to fix this, we decided to apply a quick fix based on the suggestions in this issue. - -In summary, we modified the `update` command to be able to fetch the newest version of ChromeDriver. The changes are "hacky" and only involve the following files: - -- `built/lib/binaries/chrome_xml.js`: Changed `getLatestChromeDriverVersion` to refer to the new location of chromeDriver. -- `built/lib/cmds/update.js`: Changed `unzip` function to handle the new filenames. - - -The following command was used for generating this patch: -``` -npx patch-package webdriver-manager --exclude=\.DS_Store -``` diff --git a/patches/webdriver-manager+12.1.9.patch b/patches/webdriver-manager+12.1.9.patch deleted file mode 100644 index c5ce1ba10..000000000 --- a/patches/webdriver-manager+12.1.9.patch +++ /dev/null @@ -1,86 +0,0 @@ -diff --git a/node_modules/webdriver-manager/built/lib/binaries/chrome_xml.js b/node_modules/webdriver-manager/built/lib/binaries/chrome_xml.js -index 6dfbea5..52e2fd9 100644 ---- a/node_modules/webdriver-manager/built/lib/binaries/chrome_xml.js -+++ b/node_modules/webdriver-manager/built/lib/binaries/chrome_xml.js -@@ -55,13 +55,41 @@ class ChromeXml extends config_source_1.XmlConfigSource { - return 'linux'; - } - } -+ /** -+ * Helper method, gets the ostype and osarch to return the platform name -+ */ -+ getPlatformName() { -+ const osType = this.getOsTypeName(); -+ // mac-x64 or mac-arm64 -+ if (osType === 'mac') { -+ return osType + '-' + this.osarch; -+ } -+ // win32 or win64 -+ else if (osType === 'win') { -+ return osType + (this.osarch === 'x64' ? '64' : '32'); -+ } -+ else { -+ return 'linux64'; -+ } -+ -+ } - /** - * Gets the latest item from the XML. - */ - getLatestChromeDriverVersion() { -- const latestReleaseUrl = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE'; -- return http_utils_1.requestBody(latestReleaseUrl).then(latestVersion => { -- return this.getSpecificChromeDriverVersion(latestVersion); -+ const currPlatform = this.getPlatformName(); -+ const lastKnownGoodVersionsWithDownloads_Url = 'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json'; -+ -+ return http_utils_1.requestBody(lastKnownGoodVersionsWithDownloads_Url).then(body => { -+ const latestVersion_Body = JSON.parse(body)['channels']['Stable'] -+ -+ const latestVersion = latestVersion_Body['version'] -+ const latestVersion_Url = latestVersion_Body['downloads']['chromedriver'].find(obj => obj['platform'] == currPlatform)['url'] -+ -+ return Promise.resolve({ -+ url: latestVersion_Url, -+ version: latestVersion, -+ }); - }); - } - /** -diff --git a/node_modules/webdriver-manager/built/lib/cmds/update.js b/node_modules/webdriver-manager/built/lib/cmds/update.js -index b98cdce..12c2d47 100644 ---- a/node_modules/webdriver-manager/built/lib/cmds/update.js -+++ b/node_modules/webdriver-manager/built/lib/cmds/update.js -@@ -207,6 +207,19 @@ function updateBinary(binary, outputDir, proxy, ignoreSSL) { - } - }); - } -+function getPlatformName() { -+ const osType = config_1.Config.osType(); -+ const osArch = config_1.Config.osArch(); -+ if (osType === 'Darwin') { -+ return 'mac' + '-' + osArch; -+ } -+ else if (osType === 'Windows_NT') { -+ return 'win' + (osArch === 'x64' ? '64' : '32'); -+ } -+ else { -+ return 'linux64'; -+ } -+} - function unzip(binary, outputDir, fileName) { - // remove the previously saved file and unzip it - let osType = config_1.Config.osType(); -@@ -237,7 +250,11 @@ function unzip(binary, outputDir, fileName) { - child_process.spawnSync('tar', ['zxvf', path.resolve(outputDir, fileName), '-C', outputDir]); - } - // rename -- fs.renameSync(path.resolve(outputDir, binary.zipContentName()), mv); -+ if (fileName.indexOf('chromedriver_') != -1) { -+ fs.renameSync(path.resolve(outputDir, 'chromedriver-' + getPlatformName(), binary.zipContentName()), mv) -+ } else { -+ fs.renameSync(path.resolve(outputDir, binary.zipContentName()), mv); -+ } - // set permissions - if (osType !== 'Windows_NT') { - logger.info(binary.name + ': setting permissions to 0755 for ' + mv); diff --git a/test/e2e/data_setup/config/parallel-configs/default-config.dev.json b/test/e2e/data_setup/config/parallel-configs/default-config.dev.json index c8b63f2db..f354823ec 100644 --- a/test/e2e/data_setup/config/parallel-configs/default-config.dev.json +++ b/test/e2e/data_setup/config/parallel-configs/default-config.dev.json @@ -9,7 +9,6 @@ "recordedit/domain-filter.config.json", "recordedit/foreign-key-dropdown.config.json", "recordedit/null-values.config.json", - "recordedit/submission-disabled.config.json", "recordset/add.config.json", "recordset/edit.config.json" diff --git a/test/e2e/data_setup/config/recordedit/submission-disabled.config.json b/test/e2e/data_setup/config/recordedit/submission-disabled.config.json deleted file mode 100644 index a95309d77..000000000 --- a/test/e2e/data_setup/config/recordedit/submission-disabled.config.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "catalog": {}, - "schema": { - "name": "submission-disabled", - "createNew": true, - "path": "test/e2e/data_setup/schema/recordedit/submission-disabled.json" - }, - "tables": { - "createNew": true - }, - "entities": { - "createNew": true, - "path": "test/e2e/data_setup/data/submission-disabled" - } -} diff --git a/test/e2e/data_setup/config/recordedit/submission-disabled.dev.json b/test/e2e/data_setup/config/recordedit/submission-disabled.dev.json deleted file mode 100644 index 5bc5a2405..000000000 --- a/test/e2e/data_setup/config/recordedit/submission-disabled.dev.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "setup": { - "schemaConfigurations": [ - "recordedit/submission-disabled.config.json" - ], - "schema": "submission-disabled" - }, - "cleanup" : true -} diff --git a/test/e2e/data_setup/data/submission-disabled/duplicate_key_conflict.json b/test/e2e/data_setup/data/submission-disabled/duplicate_key_conflict.json deleted file mode 100644 index c6b535fdf..000000000 --- a/test/e2e/data_setup/data/submission-disabled/duplicate_key_conflict.json +++ /dev/null @@ -1 +0,0 @@ -[{"duplicate_id": 1000}] diff --git a/test/e2e/data_setup/data/submission-disabled/submission-disabled-table.json b/test/e2e/data_setup/data/submission-disabled/submission-disabled-table.json deleted file mode 100644 index 9ccb2ebe2..000000000 --- a/test/e2e/data_setup/data/submission-disabled/submission-disabled-table.json +++ /dev/null @@ -1 +0,0 @@ -[{"id": 1000, "text": "some value", "int": 3483}] diff --git a/test/e2e/data_setup/schema/recordedit/defaults.json b/test/e2e/data_setup/schema/recordedit/defaults.json index c18c86cf9..e15f89fe9 100644 --- a/test/e2e/data_setup/schema/recordedit/defaults.json +++ b/test/e2e/data_setup/schema/recordedit/defaults.json @@ -388,7 +388,7 @@ "timestamp_disabled", "timestamptz_disabled", "json_disabled", "color_rgb_hex_disabled" ], "detailed": [ - "text", "text_disabled", "markdown", "markdown_disabled", ["defaults", "fk_text"], ["defaults", "fk_text_disabled"], "int", "int_disabled", "float", "float_disabled", + "text", "text_disabled", "markdown", "markdown_disabled", ["defaults", "fk_text"], ["defaults", "fk_dropdown"], ["defaults", "fk_text_disabled"], "int", "int_disabled", "float", "float_disabled", "boolean_true", "boolean_false", "boolean_disabled", "date", "date_disabled", "timestamp", "timestamp_disabled", "timestamp_disabled_no_default", "timestamptz", "timestamptz_disabled", "timestamptz_disabled_no_default", "json", "json_disabled", "json_disabled_no_default", "color_rgb_hex", "color_rgb_hex_disabled" ] diff --git a/test/e2e/data_setup/schema/recordedit/submission-disabled.json b/test/e2e/data_setup/schema/recordedit/submission-disabled.json deleted file mode 100644 index 37e255c2d..000000000 --- a/test/e2e/data_setup/schema/recordedit/submission-disabled.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "schema_name": "submission-disabled", - "tables": { - "submission-disabled-table": { - "comment": "Table to represent adding multiple entities", - "kind": "table", - "keys": [ - { - "comment": null, - "annotations": {}, - "unique_columns": [ - "id" - ] - } - ], - "foreign_keys": [], - "table_name": "submission-disabled-table", - "schema_name": "submission-disabled", - "column_definitions": [ - { - "name": "id", - "default": null, - "nullok": false, - "type": { - "typename": "serial4" - }, - "annotations": { - "comment": [ - "hidden" - ], - "tag:isrd.isi.edu,2016:generated": null, - "tag:isrd.isi.edu,2016:immutable": null - } - }, { - "name": "text", - "nullok": true, - "type": { - "typename": "text" - } - }, { - "name": "int", - "nullok": true, - "type": { - "typename": "int" - } - } - ], - "annotations": { - "tag:isrd.isi.edu,2016:visible-columns" : { - "*": ["id", "text", "int"] - } - } - }, - "duplicate_key_conflict": { - "kind": "table", - "table_name": "duplicate_key_conflict", - "schema_name": "submission-disabled", - "keys": [{ - "unique_columns": ["duplicate_id"] - }], - "column_definitions": [ - { - "name": "duplicate_id", - "nullok": false, - "type": {"typename":"int4"} - } - ] - } - }, - "table_names": [ - "submission-disabled-table", - "duplicate_key_conflict" - ], - "comment": null, - "annotations": {} -} diff --git a/test/e2e/locators/recordedit.ts b/test/e2e/locators/recordedit.ts index c6f4e7055..ce9758f75 100644 --- a/test/e2e/locators/recordedit.ts +++ b/test/e2e/locators/recordedit.ts @@ -216,6 +216,15 @@ export default class RecordeditLocators { return container.locator(`.column-permission-warning-${formNumber}-${columnDisplayName}`) } + /** + * only applicaple to inputs that have a "fake" placehodler: boolean, file, foriegnkey, and iframe. + */ + static getInputPlaceholderMessage(container: Locator | Page, name: string, formNumber: number): Locator { + formNumber = formNumber || 1; + const inputName = `c_${formNumber}-${name}`; + return container.locator(`.input-switch-container-${inputName}`).locator('.chaise-input-placeholder'); + } + // -------------- file input selectors --------------- // static getTextFileInputForAColumn(container: Locator | Page, name: string, formNumber: number): Locator { formNumber = formNumber || 1; diff --git a/test/e2e/setup/playwright.model.ts b/test/e2e/setup/playwright.model.ts index d61154b5d..f45060dac 100644 --- a/test/e2e/setup/playwright.model.ts +++ b/test/e2e/setup/playwright.model.ts @@ -1,11 +1,11 @@ export type TestOptions = { /** - * the name of the main spec folder. - * usages: - * - the catalog alias - * - finding the folder (for default chaise-config.js) + * the name of the main spec folder. used for locating the chaise-config.js file. If missing, we won't try to find and + * copy the chaise-config.js file into the remote location. + * + * Note: it should be defined for all the tests under "specs". It's optional so we can use this for manual testing too. */ - mainSpecName: string, + mainSpecName?: string, /** * the name of the spec (will be used for the report file) */ diff --git a/test/e2e/setup/playwright.setup.ts b/test/e2e/setup/playwright.setup.ts index 3051f8d6e..4c33c8ac5 100644 --- a/test/e2e/setup/playwright.setup.ts +++ b/test/e2e/setup/playwright.setup.ts @@ -30,7 +30,6 @@ export default async function globalSetup(config: FullConfig) { const options: TestOptions = optionsEnv; - // TODO testConfiguration is a bit different in the protractor version // grab the data files let testConfiguration; if (options.configFileName) { @@ -69,7 +68,7 @@ export default async function globalSetup(config: FullConfig) { // create the catalog try { - await createCatalog(testConfiguration, projectNames, options.mainSpecName, options.manualTestConfig); + await createCatalog(testConfiguration, projectNames, options.manualTestConfig); } catch (exp) { throw exp; } @@ -77,11 +76,13 @@ export default async function globalSetup(config: FullConfig) { // take care of chaise-config let chaiseConfigFilePath = options.chaiseConfigFilePath; // use the default - if (typeof chaiseConfigFilePath !== 'string') { + if (typeof chaiseConfigFilePath !== 'string' && options.mainSpecName) { chaiseConfigFilePath = `test/e2e/specs/${options.mainSpecName}/chaise-config.js`; } - copyChaiseConfig(chaiseConfigFilePath, options.addDefaultCatalogToChaiseConfig); + if (chaiseConfigFilePath) { + copyChaiseConfig(chaiseConfigFilePath, options.addDefaultCatalogToChaiseConfig); + } registerCallbacks(testConfiguration); } @@ -126,7 +127,7 @@ async function checkUserSessions(): Promise<{ session: any, authCookie: string } /** * create the catalog and data */ -async function createCatalog(testConfiguration: any, projectNames: string[], mainSpecName: string, isManual?: boolean) { +async function createCatalog(testConfiguration: any, projectNames: string[], isManual?: boolean) { return new Promise(async (resolve, reject) => { testConfiguration.setup.url = process.env.ERMREST_URL; diff --git a/test/e2e/specs/all-features-confirmation/errors/errors.spec.js b/test/e2e/specs/all-features-confirmation/errors/errors.spec.js deleted file mode 100644 index 0339556d4..000000000 --- a/test/e2e/specs/all-features-confirmation/errors/errors.spec.js +++ /dev/null @@ -1,114 +0,0 @@ -const { browser, element } = require('protractor'); -var chaisePage = require('../../../utils/chaise.page.js'); -var recordHelpers = require('../../../utils/record-helpers.js'); -var EC = protractor.ExpectedConditions; - -var testParams = { - table_name: "accommodation", - schemaName: "product-record", - fileTable: "file", - multipleRecordsTable : "multiple_records", - tableNotFound : "accommodation_not_found", - conflict : "Conflict", - deletionErrTextBooking : [ - "This entry cannot be deleted as it is still referenced from the booking table. " + - "All dependent entries must be removed before this item can be deleted. " + - "If you have trouble removing dependencies please contact the site administrator.\n\n" + - "Show Error Details" - ], - deletionErrTextAccommodationImg : [ - "This entry cannot be deleted as it is still referenced from the accommodation_image table. " + - "All dependent entries must be removed before this item can be deleted. " + - "If you have trouble removing dependencies please contact the site administrator.\n\n" + - "Show Error Details" - ], - recordNotFoundModalText : "The record does not exist or may be hidden.\nIf you continue to face this issue, please contact the system administrator.\n\nClick OK to show the list of all records.", - multipleRecordFoundModalText : "There are more than 1 record found for the filters provided.\n\nClick OK to show all the matched records.", - tableNotFoundModalText : function() { return "Table " + this.tableNotFound + " not found in schema.\n\nClick OK to go to the Home Page."}, - sizeNotValidModalText : function() { return "'limit' must be greater than 0\n\nClick OK to go to the " + this.multipleRecordsTable + "."}, - negativeLimitErrorText : "'limit' must be greater than 0\n\nClick OK to go to the Home Page.", - hideErrors : "Hide Error Details", - conflictRecordEditErrorBooking : "This entry cannot be deleted as it is still referenced from the booking table. All dependent entries must be removed before this item can be deleted.\n\nClick OK to go to the Accommodations.\nClick Reload to start over.\nShow Error Details", - conflictRecordEditErrorAccommodationImg : "This entry cannot be deleted as it is still referenced from the accommodation_image table. All dependent entries must be removed before this item can be deleted.\n\nClick OK to go to the Accommodations.\nClick Reload to start over.\nShow Error Details", - facetErrorstext:{ - invalidPageCriteriaTitle: "Invalid Page Criteria", - invalidPageCriteriaBody: "Click OK to reload this page without Invalid Page Criteria.", - invalidFacetFilterTitle: "Invalid Facet Filters", - invalidFacetFilterBody: "Click OK to reload this page without Invalid Facet Filters.", - invalidFilterOperatorErrorTitle : "Invalid Filter", - invalidFilterOperatorErrorBody : "Click OK to show the list of all records." - }, - conflictErrors: { - title: "Conflict", - message: [ - "An unexpected error has occurred. Try clearing your cache.", - "If you continue to face this issue, please contact the system administrator.", - "\nClick OK to go to the Home Page.", - "Show Error Details" - ].join("\n"), - details: "invalid input syntax for type boolean: \"12\"", - } -}; - -/* -* All error related test cases are created here. -* Test cases from other apps would be collated in future -*/ - -describe('Error related test cases,', function() { - describe("Error check for invalid filter in RecordEdit", function(){ - - beforeAll(function() { - pageTestUrl = browser.params.url + "/recordedit/#" + browser.params.catalogId + "/" + testParams.schemaName + ":" + testParams.table_name + "/id::gt:2002@after()"; - chaisePage.navigate(pageTestUrl); - modalTitle = chaisePage.recordEditPage.getModalTitle(); - modalActionBody = chaisePage.recordEditPage.getModalActionBody(); - }); - - it("should be returned Invalid Page Criteria", function (done) { - chaisePage.waitForElement(modalTitle).then(function(){ - return modalTitle.getText(); - }).then(function (text) { - expect(text).toBe(testParams.facetErrorstext.invalidPageCriteriaTitle, "Invalid Page Criteria error pop-up could not be opened!"); - return modalActionBody.getText(); - }).then(function (errorText) { - expect(errorText).toBe(testParams.facetErrorstext.invalidPageCriteriaBody, "Error action text did not match"); - done(); - }).catch(chaisePage.catchTestError(done)); - }); - - it('On click of OK button the page should reload the page without paging condition but with invalid filter conditions', function(done){ - const modalOkBtn = chaisePage.errorModal.getOKButton(); - // make sure ok button is clickable - browser.wait(protractor.ExpectedConditions.elementToBeClickable(modalOkBtn), browser.params.defaultTimeout); - chaisePage.clickButton(modalOkBtn).then(function(){ - return chaisePage.waitForElement(modalTitle); - }).then (function (){ - return modalTitle.getText(); - }).then(function (text) { - expect(text).toBe(testParams.facetErrorstext.invalidFilterOperatorErrorTitle, "Invalid Filter operator error pop-up could not be opened!"); - return modalActionBody.getText(); - }).then(function (errorText) { - expect(errorText).toBe(testParams.facetErrorstext.invalidFilterOperatorErrorBody, "Error action text did not match"); - done(); - }).catch(chaisePage.catchTestError(done)); - }); - - it('On click of OK button the page should redirect to RecordSet', function(done){ - const modalOkBtn = chaisePage.errorModal.getOKButton(); - // make sure ok button is clickable - browser.wait(protractor.ExpectedConditions.elementToBeClickable(modalOkBtn), browser.params.defaultTimeout); - chaisePage.clickButton(modalOkBtn).then(function(){ - recordsetWithoutFacetUrl = browser.params.url + "/recordset/#" + browser.params.catalogId + "/" + testParams.schemaName + ":" + testParams.table_name; - browser.wait(() => { - return browser.driver.getCurrentUrl().then((url) => { - return url.toContain(recordsetWithoutFacetUrl); - }); - }); - - done(); - }).catch(chaisePage.catchTestError(done)); - }); - - }); -}); diff --git a/test/e2e/specs/all-features-confirmation/errors/errors.spec.ts b/test/e2e/specs/all-features-confirmation/errors/errors.spec.ts index 7f123f3c1..515b4ee3f 100644 --- a/test/e2e/specs/all-features-confirmation/errors/errors.spec.ts +++ b/test/e2e/specs/all-features-confirmation/errors/errors.spec.ts @@ -1,5 +1,5 @@ import { test, expect, TestInfo, Page, Locator } from '@playwright/test'; -import RecordeditLocators from '@isrd-isi-edu/chaise/test/e2e/locators/recordedit'; +import RecordeditLocators, { RecordeditInputType } from '@isrd-isi-edu/chaise/test/e2e/locators/recordedit'; import RecordLocators from '@isrd-isi-edu/chaise/test/e2e/locators/record'; import RecordsetLocators from '@isrd-isi-edu/chaise/test/e2e/locators/recordset'; import ModalLocators from '@isrd-isi-edu/chaise/test/e2e/locators/modal'; @@ -7,6 +7,7 @@ import AlertLocators from '@isrd-isi-edu/chaise/test/e2e/locators/alert'; import { getCatalogID } from '@isrd-isi-edu/chaise/test/e2e/utils/catalog-utils'; import { removeAuthCookieAndReload } from '@isrd-isi-edu/chaise/test/e2e/utils/user-utils'; +import { setInputValue } from '@isrd-isi-edu/chaise/test/e2e/utils/recordedit-utils'; test.describe('error handling', () => { // run all test cases in here in parallel @@ -54,7 +55,6 @@ test.describe('error handling', () => { await page.waitForURL('**' + getChaiseURL('recordset', 'accommodation', baseURL, testInfo) + '**'); }); - // why are we testing this? this was in the original protractor test await test.step('clicking back button should go back to the initial page', async () => { await page.goBack(); await expect.soft(page).toHaveURL(recordPageURL); @@ -244,7 +244,6 @@ test.describe('error handling', () => { }); test.describe('recordedit app', () => { - test('navigating to a page with that returns no record', async ({ page, baseURL }, testInfo) => { const errorModal = ModalLocators.getErrorModal(page); const recordPageURL = getChaiseURL('recordedit', 'accommodation', baseURL, testInfo) + '/id=11223312121'; @@ -333,6 +332,57 @@ test.describe('error handling', () => { }); }); + + test('editting a record without changing data', async ({ baseURL, page }, testInfo) => { + await test.step('open recordedit page', async () => { + await page.goto(getChaiseURL('recordedit', 'category', baseURL, testInfo) + '/id=10003'); + await RecordeditLocators.waitForRecordeditPageReady(page); + }); + + await test.step('an alert should be displayed upon submission', async () => { + const alert = AlertLocators.getWarningAlert(page); + await RecordeditLocators.submitForm(page); + await expect.soft(alert).toBeVisible(); + await expect.soft(alert).toHaveText([ + 'WarningNo data was changed in the update request. ', + 'Please check the form content and resubmit the data.' + ].join('')); + }); + }); + + test('duplicate key value and required errors while creating a record', async ({ baseURL, page }, testInfo) => { + const recordeditError = AlertLocators.getErrorAlert(page); + await test.step('open recordedit page', async () => { + await page.goto(getChaiseURL('recordedit', 'category', baseURL, testInfo)); + await RecordeditLocators.waitForRecordeditPageReady(page); + }); + + await test.step('submitting without setting values should result in the required error', async () => { + await RecordeditLocators.submitForm(page); + await expect.soft(recordeditError).toBeVisible(); + await expect.soft(recordeditError).toHaveText([ + 'ErrorSorry, the data could not be submitted because there are errors on the form. ', + 'Please check all fields and try again.' + ].join('')); + const requiredError = 'Please enter a value for this field.'; + await expect.soft(RecordeditLocators.getErrorMessageForAColumn(page, 'id', 1)).toHaveText(requiredError); + await expect.soft(RecordeditLocators.getErrorMessageForAColumn(page, 'term', 1)).toHaveText(requiredError); + }); + + await test.step('submitting after using a duplicate key value should return in the duplicate error.', async () => { + await setInputValue(page, 1, 'id', 'id', RecordeditInputType.INT_4, '99999'); + await setInputValue(page, 1, 'term', 'term', RecordeditInputType.TEXT, 'Castle'); + await RecordeditLocators.submitForm(page); + await expect.soft(recordeditError).toBeVisible(); + await expect.soft(recordeditError).toHaveText([ + 'ErrorThe entry cannot be created/updated. ', + 'Please use a different term for this record. ', + 'Click here to see the conflicting record that already exists.' + ].join('')); + + }); + + }); }); }); diff --git a/test/e2e/specs/default-config/protractor.conf.js b/test/e2e/specs/default-config/protractor.conf.js deleted file mode 100644 index 5c0320020..000000000 --- a/test/e2e/specs/default-config/protractor.conf.js +++ /dev/null @@ -1,16 +0,0 @@ -var pConfig = require('./../../utils/protractor.configuration.js'); - -var config = pConfig.getConfig({ - // This config is meant to be run as part of the parallel tests configuration - configFileName: 'parallel-configs/default-config.dev.json', - specs: [ - "*/*.spec.js" - ], - setBaseUrl: function(browser, data) { - browser.params.url = process.env.CHAISE_BASE_URL; - return browser.params.url; - }, - chaiseConfigFilePath: 'test/e2e/specs/default-config/chaise-config.js' -}); - -exports.config = config; diff --git a/test/e2e/specs/default-config/recordedit/immutable-inputs.conf.js b/test/e2e/specs/default-config/recordedit/immutable-inputs.conf.js deleted file mode 100644 index aec1de6c3..000000000 --- a/test/e2e/specs/default-config/recordedit/immutable-inputs.conf.js +++ /dev/null @@ -1,15 +0,0 @@ -var pConfig = require('./../../../utils/protractor.configuration.js'); - -var config = pConfig.getConfig({ - configFileName: 'recordedit/immutable-inputs.dev.json', - chaiseConfigFilePath: 'test/e2e/specs/default-config/chaise-config.js', - specs: [ - "immutable-inputs.spec.js" - ], - setBaseUrl: function(browser, data) { - browser.params.url = process.env.CHAISE_BASE_URL; - return browser.params.url; - } -}); - -exports.config = config; diff --git a/test/e2e/specs/default-config/recordedit/immutable-inputs.config.ts b/test/e2e/specs/default-config/recordedit/immutable-inputs.config.ts new file mode 100644 index 000000000..76c5a8170 --- /dev/null +++ b/test/e2e/specs/default-config/recordedit/immutable-inputs.config.ts @@ -0,0 +1,8 @@ +import getConfig from '@isrd-isi-edu/chaise/test/e2e/setup/playwright.configuration'; + +export default getConfig({ + testName: 'default-config/recordedit/immutable-inputs', + configFileName: 'recordedit/immutable-inputs.dev.json', + mainSpecName: 'default-config', + testMatch: [ 'immutable-inputs.spec.ts' ] +}); diff --git a/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.js b/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.js deleted file mode 100644 index 9ce1edf4c..000000000 --- a/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.js +++ /dev/null @@ -1,401 +0,0 @@ -var chaisePage = require('../../../utils/chaise.page.js'); -var recordEditHelpers = require('../../../utils/recordedit-helpers.js'); -var momentTz = require('moment-timezone'); -var testParams = { - // for verifying data is present - column_names: [ - "text", "text_disabled", "markdown", "markdown_disabled", "iKS50idGfVCGnnS6lUoZ8Q", "WnsyE4pJ1O0IW8zsj6MDHg", "int", "int_disabled", - "float", "float_disabled", "boolean_true", "boolean_false", "boolean_disabled", "date", "date_disabled", "timestamp", "timestamp_disabled", - "timestamptz", "timestamptz_disabled", "json", "json_disabled", "color_rgb_hex", "color_rgb_hex_disabled" - ], - table_name: "defaults-table", - default_column_values: { - // data values - text_value: "default", - text_disabled_value: "Disabled input", - markdown_value: "**bold**", - markdown_disabled_value: "*italics*", - foreign_key_value: "Default for foreign_key column", // rowname of the fk - foreign_key_dropdown_value: "Default for foreign_key_dropdown column", // rowname of the fk - foreign_key_disabled_value: "Default for foreign_key_disabled column", // rowname of the disabled fk - int_value: "25", - int_disabled_value: "20", - float_value: "1.6478", - float_disabled_value: "93.2182", - boolean_true_value: "true", - boolean_false_value: "false", - boolean_disabled_value: "false", - date_value: "2010-06-08", - date_disabled_value: "2014-05-12", - timestamp_value: "2016-05-14T17:30:00", - timestamp_date_value: "2016-05-14", - timestamp_time_value: "17:30:00", - timestamp_disabled_value: "2012-06-22T18:36:00", - timestamp_disabled_date_value: "2012-06-22", - timestamp_disabled_time_value: "18:36:00", - timestamp_disabled_no_default_value: "", - timestamp_disabled_no_default_date_value: "", - timestamp_disabled_no_default_time_value: "", - timestamptz_value: "2014-05-07T14:40:00-07:00", - timestamptz_date_value: "2014-05-07", - timestamptz_time_value: "14:40:00", - timestamptz_disabled_value: "2010-06-13T17:22:00-07:00", - timestamptz_disabled_date_value: "2010-06-13", - timestamptz_disabled_time_value: "17:22:00", - timestamptz_disabled_no_default_value: "", - timestamptz_disabled_no_default_date_value: "", - timestamptz_disabled_no_default_time_value: "", - json_value:JSON.stringify({"name":"testing_json"}), - json_disabled_value:JSON.stringify(98.786), - json_disabled_no_default_value: "Automatically generated", - asset_value: "28110_191_z.jpg", - asset_disabled_value: "28110_191_z.jpg", - asset_disabled_no_default_value: "Automatically generated", - color_rgb_hex_value: "#123456", - color_rgb_hex_disabled_value: "#654321", - rid_disabled_value: "Automatically generated", - rcb_disabled_value: "Automatically generated", - rmb_disabled_value: "Automatically generated", - rct_disabled_date_value: "Automatically generated", - rct_disabled_time_value: "Automatically generated", - rmt_disabled_date_value: "Automatically generated", - rmt_disabled_time_value: "Automatically generated", - }, - record_column_values: { - // data values - text: "default", - text_disabled: "Disabled input", - markdown: "bold", - markdown_disabled: "italics", - // Value of "name" column on foreign (defaults_fk_text) related entity - "iKS50idGfVCGnnS6lUoZ8Q": "Default for foreign_key column", - // Value of "name" column on foreign (defaults_fk_text_dropdown) related entity - "2PO3pruPa9O5g7nNztzMjQ": "Default for foreign_key_dropdown column", - // Value of "name" column on foreign (defaults_fk_text_disabled) related entity - "WnsyE4pJ1O0IW8zsj6MDHg": "Default for foreign_key_disabled column", - int: "25", - int_disabled: "20", - float: "1.6478", - float_disabled: "93.2182", - boolean_true: "true", - boolean_false: "false", - boolean_disabled: "false", - date: "2010-06-08", - date_disabled: "2014-05-12", - timestamp: "2016-05-14 17:30:00", - timestamp_disabled: "2012-06-22 18:36:00", - timestamptz: "2014-05-07 14:40:00", - timestamptz_disabled: "2010-06-13 17:22:00", - json: JSON.stringify({"name":"testing_json"},undefined,2), - json_disabled: JSON.stringify(98.786), - color_rgb_hex: "#123456", - color_rgb_hex_disabled: "#654321" - }, - edit_key: { name: "id", value: "2", operator: "="}, - re_column_names: [ - { name: "text_disabled" }, - { name: "markdown_disabled", type: "textarea" }, - { name: "foreign_key_disabled", type: "foreign_key" }, - { name: "int_disabled" }, - { name: "float_disabled" }, - { name: "boolean_disabled", type: "dropdown" }, - { name: "date_disabled" }, - { name: "timestamp_disabled", type: "timestamp" }, - { name: "timestamptz_disabled", type: "timestamp" }, - { name: "json_disabled", type: "textarea" }, - { name: "asset_disabled", type: "asset" }, - { name: "color_rgb_hex_disabled", type: "color" } - ], - re_column_values: { - text_disabled: "Disabled input", - markdown_disabled: "*italics*", - // Value of "name" column on foreign related entity - foreign_key_disabled: "Default for foreign_key_disabled column", - int_disabled: "20", - float_disabled: "93.2182", - boolean_disabled: "false", - date_disabled: "2014-05-12", - timestamp_disabled: "2012-06-22T18:36:00", - timestamp_disabled_date: "2012-06-22", - timestamp_disabled_time: "18:36:00", - timestamptz_disabled: "2010-06-13T17:22:00-07:00", - timestamptz_disabled_date: "2010-06-13", - timestamptz_disabled_time: "17:22:00", - json_disabled: JSON.stringify(98.786), - // Value of "filename" column for the current record - // asset_disabled: "Four Points Sherathon 3" - asset_disabled: "28110_191_z.jpg", - color_rgb_hex_disabled: "#654321" - } -}; - -describe('Record Add with defaults', function() { - - describe("for when the user creates an entity with default values, ", function() { - var EC = protractor.ExpectedConditions, - values = testParams.default_column_values, - textInput, textDisabledInput, - markdownInput, markdownDisabledInput, - foreignKeyInput, foreignKeyDropdownInput, foreignKeyDisabledInput, - intInput, intDisabledInput, - floatInput, floatDisabledInput, - booleanTrueInput, booleanFalseInput, booleanDisabledInput, - dateInput, dateDisabledInput, - timestampInputs, timestampDisabledInput, timestampDisabledNoDefaultInput, - timestamptzInputs, timestamptzDisabledInput, timestamptzDisabledNoDefaultInput, - jsonInput, jsonDisabledInput, jsonDisabledNoDefaultInput, - colorRGBHexInput, colorRGBHexDisabledInput; - - beforeAll(function () { - chaisePage.navigate(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/defaults:" + testParams.table_name); - chaisePage.waitForElement(element(by.id("submit-record-button"))); - }); - - it("should prefill simple input fields that are not disabled with their default value.", function() { - textInput = chaisePage.recordEditPage.getInputForAColumn('text', 1); - markdownInput = chaisePage.recordEditPage.getTextAreaForAColumn('markdown', 1); - intInput = chaisePage.recordEditPage.getInputForAColumn('int', 1); - floatInput = chaisePage.recordEditPage.getInputForAColumn('float', 1); - booleanTrueInput = chaisePage.recordEditPage.getDropdownElementByName('boolean_true', 1); - booleanFalseInput = chaisePage.recordEditPage.getDropdownElementByName('boolean_false', 1); - dateInput = chaisePage.recordEditPage.getInputForAColumn('date', 1); - jsonInput = chaisePage.recordEditPage.getTextAreaForAColumn('json', 1); - colorRGBHexInput = chaisePage.recordEditPage.getColorInputForAColumn('color_rgb_hex', 1); - - expect(textInput.getAttribute("value")).toBe(values.text_value, "Text input default is incorrect"); - expect(markdownInput.getAttribute("value")).toBe(values.markdown_value, "Markdown input default is incorrect"); - expect(intInput.getAttribute("value")).toBe(values.int_value, "Int input default is incorrect"); - expect(floatInput.getAttribute("value")).toBe(values.float_value, "Float input default is incorrect"); - expect(chaisePage.recordEditPage.getDropdownText(booleanTrueInput).getText()).toBe(values.boolean_true_value, "Boolean input is not set to true"); - expect(chaisePage.recordEditPage.getDropdownText(booleanFalseInput).getText()).toBe(values.boolean_false_value, "Boolean input is not set to false"); - expect(dateInput.getAttribute("value")).toBe(values.date_value, "Date input default is incorrect"); - expect(jsonInput.getAttribute("value")).toBe(values.json_value, "JSON input default is incorrect"); - expect(colorRGBHexInput.getAttribute("value")).toBe(values.color_rgb_hex_value, "Text input default is incorrect"); - }); - - it("should prefill simple input fields that are disabled with their default value.", function() { - textDisabledInput = chaisePage.recordEditPage.getInputForAColumn('text_disabled', 1); - markdownDisabledInput = chaisePage.recordEditPage.getTextAreaForAColumn('markdown_disabled', 1); - intDisabledInput = chaisePage.recordEditPage.getInputForAColumn('int_disabled', 1); - floatDisabledInput = chaisePage.recordEditPage.getInputForAColumn('float_disabled', 1); - booleanDisabledInput = chaisePage.recordEditPage.getDropdownElementByName('boolean_disabled', 1); - dateDisabledInput = chaisePage.recordEditPage.getInputForAColumn('date_disabled', 1); - jsonInputDisabled= chaisePage.recordEditPage.getTextAreaForAColumn('json_disabled', 1); - colorRGBHexDisabledInput = chaisePage.recordEditPage.getColorInputForAColumn('color_rgb_hex_disabled', 1); - - expect(textDisabledInput.getAttribute("value")).toBe(values.text_disabled_value, "Text disabled input default is incorrect"); - expect(markdownDisabledInput.getAttribute("value")).toBe(values.markdown_disabled_value, "Markdown disabled input default is incorrect"); - expect(intDisabledInput.getAttribute("value")).toBe(values.int_disabled_value, "Int disabled input default is incorrect"); - expect(floatDisabledInput.getAttribute("value")).toBe(values.float_disabled_value, "Float disabled input default is incorrect"); - expect(chaisePage.recordEditPage.getDropdownText(booleanDisabledInput).getText()).toBe(values.boolean_disabled_value, "Boolean disabled input default is incorrect"); - expect(dateDisabledInput.getAttribute("value")).toBe(values.date_disabled_value, "Date disabled input default is incorrect"); - expect(jsonInputDisabled.getAttribute("value")).toBe(values.json_disabled_value, "JSON disabled input default is incorrect"); - expect(colorRGBHexDisabledInput.getAttribute("value")).toBe(values.color_rgb_hex_disabled_value, "Text input default is incorrect"); - - }); - - //JOSN columns - it("should initialize json columns properly if they are disabled without a default.", function() { - jsonDisabledNoDefaultInput = chaisePage.recordEditPage.getTextAreaForAColumn('json_disabled_no_default', 1); - - expect(jsonDisabledNoDefaultInput.getAttribute("value")).toBe("", "The disabled json value is incorrect"); - expect(jsonDisabledNoDefaultInput.getAttribute("placeholder")).toBe(values.json_disabled_no_default_value, "The disabled json placeholder is incorrect"); - }); - - // Timestamp columns - it("should intialize timestamp columns properly with a default value.", function() { - timestampInput = chaisePage.recordEditPage.getInputForAColumn('timestamp', 1); - timestampInputs = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamp', 1); - - expect(timestampInput.getAttribute('value')).toBe(values.timestamp_value, "Timestamp default is incorrect"); - expect(timestampInputs.date.getAttribute('value')).toBe(values.timestamp_date_value, "Timestamp date default is incorrect"); - expect(timestampInputs.time.getAttribute('value')).toBe(values.timestamp_time_value, "Timestamp time default is incorrect"); - - timestampDisabledInput = chaisePage.recordEditPage.getInputForAColumn('timestamp_disabled', 1); - timestampDisabledInputObj = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamp_disabled', 1); - - expect(timestampDisabledInput.getAttribute('value')).toBe(values.timestamp_disabled_value, "Timestamp disabled value is incorrect"); - expect(timestampDisabledInputObj.date.getAttribute('value')).toBe(values.timestamp_disabled_date_value, "Timestamp disabled date value is incorrect"); - expect(timestampDisabledInputObj.time.getAttribute('value')).toBe(values.timestamp_disabled_time_value, "Timestamp disabled time value is incorrect"); - }); - - it("should initialize timestamp columns properly if they are disabled without a default.", function() { - timestampDisabledNoDefaultInput = chaisePage.recordEditPage.getInputForAColumn('timestamp_disabled_no_default', 1); - timestampDisabledNoDefaultInputObj = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamp_disabled_no_default', 1); - - // should both be empty string ("") - expect(timestampDisabledNoDefaultInput.getAttribute("value")).toBe(values.timestamp_disabled_no_default_value, "The disabled timestamp value is incorrect"); - expect(timestampDisabledNoDefaultInputObj.date.getAttribute("value")).toBe(values.timestamp_disabled_no_default_date_value, "The disabled timestamp date value is incorrect"); - expect(timestampDisabledNoDefaultInputObj.time.getAttribute("value")).toBe(values.timestamp_disabled_no_default_time_value, "The disabled timestamp time value is incorrect"); - }); - - // Timestamptz columns - it("should intialize timestamptz columns properly with a default value.", function() { - timestamptzInput = chaisePage.recordEditPage.getInputForAColumn('timestamptz', 1); - timestamptzInputs = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamptz', 1); - - expect(timestamptzInput.getAttribute('value')).toBe(values.timestamptz_value, "Timestamptz default is incorrect"); - expect(timestamptzInputs.date.getAttribute('value')).toBe(values.timestamptz_date_value, "Timestamptz date default is incorrect"); - expect(timestamptzInputs.time.getAttribute('value')).toBe(values.timestamptz_time_value, "Timestamptz time default is incorrect"); - - timestamptzDisabledInput = chaisePage.recordEditPage.getInputForAColumn('timestamptz_disabled', 1); - timestamptzDisabledInputObj = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamptz_disabled', 1); - - expect(timestamptzDisabledInput.getAttribute('value')).toBe(values.timestamptz_disabled_value, "Hidden timestamptz disabled value is incorrect"); - expect(timestamptzDisabledInputObj.date.getAttribute('value')).toBe(values.timestamptz_disabled_date_value, "Timestamptz disabled date value is incorrect"); - expect(timestamptzDisabledInputObj.time.getAttribute('value')).toBe(values.timestamptz_disabled_time_value, "Timestamptz disabled time value is incorrect"); - }); - - it("should initialize timestamptz columns properly if they are disabled without a default.", function() { - timestamptzDisabledNoDefaultInput = chaisePage.recordEditPage.getInputForAColumn('timestamptz_disabled_no_default', 1); - timestamptzDisabledNoDefaultInputObj = chaisePage.recordEditPage.getTimestampInputsForAColumn('timestamptz_disabled_no_default', 1); - - expect(timestamptzDisabledNoDefaultInput.getAttribute('value')).toBe(values.timestamptz_disabled_no_default_value, "Hidden timestamptz disabled value is incorrect"); - expect(timestamptzDisabledNoDefaultInputObj.date.getAttribute("value")).toBe(values.timestamptz_disabled_no_default_date_value, "The disabled timestamptz date value is incorrect"); - expect(timestamptzDisabledNoDefaultInputObj.time.getAttribute("value")).toBe(values.timestamptz_disabled_no_default_time_value, "The disabled timestamptz time value is incorrect"); - }); - - // Foreign key columns - it("should initialize foreign key inputs with their default value.", function() { - // the clone will be disabled while data is loading. - browser.wait(EC.elementToBeClickable(chaisePage.recordEditPage.getCloneFormInputSubmitButton())); - - foreignKeyInput = chaisePage.recordEditPage.getForeignKeyInputDisplay("foreign_key", 1); - foreignKeyDropdownInput = chaisePage.recordEditPage.getForeignKeyInputDisplay("foreign_key_dropdown", 1); - foreignKeyDisabledInput = chaisePage.recordEditPage.getForeignKeyInputDisplay("foreign_key_disabled", 1); - - expect(foreignKeyInput.getText()).toBe(values.foreign_key_value, "Foreign key input default is incorrect"); - expect(foreignKeyDropdownInput.getText()).toBe(values.foreign_key_dropdown_value, "Foreign key dropdown input default is incorrect"); - expect(foreignKeyDisabledInput.getText()).toBe(values.foreign_key_disabled_value, "Foreign key disabled default is incorrect"); - }); - - // Asset columns - it("should initialize asset column inputs with their default value.", function() { - // the clone btn will be disabled while data is loading. - browser.wait(EC.elementToBeClickable(chaisePage.recordEditPage.getCloneFormInputSubmitButton())); - - const assetTextInput = chaisePage.recordEditPage.getTextFileInputForAColumn("asset", 1) - expect(assetTextInput.getText()).toBe(values.asset_value, "Asset input default is incorrect"); - - const assetDisabledTextInput = chaisePage.recordEditPage.getTextFileInputForAColumn("asset_disabled", 1); - expect(assetDisabledTextInput.getText()).toBe(values.asset_disabled_value, "Asset disabled default is incorrect"); - }); - - it("should initialize asset columns properly if they are disabled without a default.", function() { - const assetTextInput = chaisePage.recordEditPage.getTextFileInputForAColumn("asset_disabled_no_default", 1); - expect(assetTextInput.getText()).toBe(values.asset_disabled_no_default_value, "The disabled asset placeholder is incorrect"); - }); - - // System columns - it("should initialize system column inputs with 'Automatically Generated'.", function() { - var ridDisabledInput = chaisePage.recordEditPage.getInputForAColumn("RID", 1), - rcbDisabledInput = chaisePage.recordEditPage.getInputForAColumn("RCB", 1), - rmbDisabledInput = chaisePage.recordEditPage.getInputForAColumn("RMB", 1), - rctDisabledInput = chaisePage.recordEditPage.getTimestampInputsForAColumn("RCT", 1), - rmtDisabledInput = chaisePage.recordEditPage.getTimestampInputsForAColumn("RMT", 1); - - expect(ridDisabledInput.getAttribute("placeholder")).toBe(values.rid_disabled_value, "RID disabled input default is incorrect"); - expect(rcbDisabledInput.getAttribute("placeholder")).toBe(values.rcb_disabled_value, "RCB disabled input default is incorrect"); - expect(rmbDisabledInput.getAttribute("placeholder")).toBe(values.rmb_disabled_value, "RMB disabled input default is incorrect"); - - expect(rctDisabledInput.date.getAttribute("placeholder")).toBe(values.rct_disabled_date_value, "RCT disabled date input default is incorrect"); - expect(rctDisabledInput.time.getAttribute("placeholder")).toBe(values.rct_disabled_time_value, "RCT disabled time input default is incorrect"); - - expect(rmtDisabledInput.date.getAttribute("placeholder")).toBe(values.rmt_disabled_date_value, "RMT disabled date input default is incorrect"); - expect(rmtDisabledInput.time.getAttribute("placeholder")).toBe(values.rmt_disabled_time_value, "RMT disabled time input default is incorrect"); - }); - - // TODO write tests for default values for composite foreign keys when implemented - - describe("Submit the form", function() { - beforeAll(function() { - chaisePage.recordEditPage.submitForm(); - }); - - it("and redirect to a record page with the default values.", function() { - var redirectUrl = browser.params.url + "/record/#" + browser.params.catalogId + "/defaults:" + testParams.table_name + "/RID="; - - browser.wait(function () { - return browser.driver.getCurrentUrl().then(function(url) { - return url.startsWith(redirectUrl); - }); - }); - - expect(browser.driver.getCurrentUrl()).toContain(redirectUrl); - - recordEditHelpers.testRecordAppValuesAfterSubmission(testParams.column_names, testParams.record_column_values, testParams.column_names.length); - }); - }); - }); -}); - -describe("Record Edit with immutable columns", function() { - - describe("should verify the presentation of data", function () { - - beforeAll(function () { - var keys = []; - keys.push(testParams.edit_key.name + testParams.edit_key.operator + testParams.edit_key.value); - chaisePage.navigate(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/defaults:" + testParams.table_name + "/" + keys.join("&")); - - chaisePage.recordeditPageReady(); - browser.wait(function() { - return chaisePage.recordEditPage.getAllColumnNames().count().then(function(ct) { - return (ct == testParams.re_column_names.length+1); // first row is not included - }); - }, browser.params.defaultTimeout); - }); - - for (var i=0; i < testParams.re_column_names.length; i++) { - (function(index) { - const columnObj = testParams.re_column_names[index]; - const columnName = columnObj.name; - // normal inputs with values in input under value attribute - it("should initialize text input column: " + columnName + " with the proper value", function () { - let input; - switch (columnObj.type) { - case 'asset': - input = chaisePage.recordEditPage.getTextFileInputForAColumn(columnName, 1); - expect(input.getText()).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - - // that's why the asset column must be added to the begining of visible columns list - const tooltip = chaisePage.getTooltipDiv(); - chaisePage.waitForElementInverse(tooltip).then(function () { - chaisePage.testTooltipReturnPromise(input, testParams.re_column_values[columnName], 'recordedit'); - }); - break; - case 'color': - input = chaisePage.recordEditPage.getColorInputForAColumn(columnName, 1); - expect(input.getAttribute('value')).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - break; - case 'dropdown': - input = chaisePage.recordEditPage.getDropdownElementByName(columnName, 1); - expect(chaisePage.recordEditPage.getDropdownText(input).getText()).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - break; - case 'foreign_key': - input = chaisePage.recordEditPage.getForeignKeyInputDisplay(columnName, 1); - expect(input.getText()).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - break; - case 'textarea': - input = chaisePage.recordEditPage.getTextAreaForAColumn(columnName, 1); - expect(input.getAttribute('value')).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - break; - case 'timestamp': - input = chaisePage.recordEditPage.getInputForAColumn(columnName, 1); - expect(input.getAttribute('value')).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - - const inputObj = chaisePage.recordEditPage.getTimestampInputsForAColumn(columnName, 1); - expect(inputObj.date.getAttribute('value')).toBe(testParams.re_column_values[columnName + '_date'], "Recordedit value for: " + columnName + " date is incorrect"); - expect(inputObj.time.getAttribute('value')).toBe(testParams.re_column_values[columnName + '_time'], "Recordedit value for: " + columnName + " time is incorrect"); - break; - default: - // colummObj.type = input but not set - input = chaisePage.recordEditPage.getInputForAColumn(columnName, 1); - expect(input.getAttribute('value')).toBe(testParams.re_column_values[columnName], "Recordedit value for: " + columnName + " is incorrect"); - break; - } - }); - })(i); - }; - }); -}); diff --git a/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.ts b/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.ts new file mode 100644 index 000000000..fd937c8aa --- /dev/null +++ b/test/e2e/specs/default-config/recordedit/immutable-inputs.spec.ts @@ -0,0 +1,210 @@ +import { expect, Locator, test } from '@playwright/test'; +import RecordeditLocators, { RecordeditInputType } from '@isrd-isi-edu/chaise/test/e2e/locators/recordedit'; +import { getCatalogID } from '@isrd-isi-edu/chaise/test/e2e/utils/catalog-utils'; +import { testInputValue, testSubmission } from '@isrd-isi-edu/chaise/test/e2e/utils/recordedit-utils'; + + +const testParams = { + schemaTable: 'defaults:defaults-table', + create: { + columns: [ + { name: 'asset_disabled', displayname: 'asset_disabled', type: RecordeditInputType.FILE, value: '28110_191_z.jpg', disabled: false }, + { name: 'asset_disabled_no_default', displayname: 'asset_disabled_no_default', type: RecordeditInputType.FILE, disabled: true }, + + { name: 'text', displayname: 'text', type: RecordeditInputType.TEXT, value: 'default', disabled: false }, + { name: 'text_disabled', displayname: 'text_disabled', type: RecordeditInputType.TEXT, value: 'Disabled input', disabled: true }, + + { name: 'markdown', displayname: 'markdown', type: RecordeditInputType.MARKDOWN, value: '**bold**', disabled: false }, + { name: 'markdown_disabled', displayname: 'markdown_disabled', type: RecordeditInputType.MARKDOWN, value: '*italics*', disabled: true }, + + { + name: 'iKS50idGfVCGnnS6lUoZ8Q', displayname: 'foreign_key', type: RecordeditInputType.FK_POPUP, + value: 'Default for foreign_key column', disabled: false + }, + { + name: '2PO3pruPa9O5g7nNztzMjQ', displayname: 'foreign_key_dropdown', type: RecordeditInputType.FK_DROPDOWN, + value: 'Default for foreign_key_dropdown column', disabled: false + }, + { + name: 'WnsyE4pJ1O0IW8zsj6MDHg', displayname: 'foreign_key_disabled', type: RecordeditInputType.FK_POPUP, + value: 'Default for foreign_key_disabled column', disabled: true + }, + + { name: 'int', displayname: 'int', type: RecordeditInputType.INT_4, value: '25', disabled: false }, + { name: 'int_disabled', displayname: 'int_disabled', type: RecordeditInputType.INT_4, value: '20', disabled: true }, + + { name: 'float', displayname: 'float', type: RecordeditInputType.NUMBER, value: '1.6478', disabled: false }, + { name: 'float_disabled', displayname: 'int_disabled', type: RecordeditInputType.NUMBER, value: '93.2182', disabled: true }, + + { name: 'boolean_true', displayname: 'boolean_true', type: RecordeditInputType.BOOLEAN, value: 'true', disabled: false }, + { name: 'boolean_false', displayname: 'boolean_false', type: RecordeditInputType.BOOLEAN, value: 'false', disabled: false }, + { name: 'boolean_disabled', displayname: 'boolean_disabled', type: RecordeditInputType.BOOLEAN, value: 'false', disabled: true }, + + { name: 'date', displayname: 'date', type: RecordeditInputType.DATE, value: '2010-06-08', disabled: false }, + { name: 'date_disabled', displayname: 'date_disabled', type: RecordeditInputType.DATE, value: '2014-05-12', disabled: true }, + + { + name: 'timestamp', displayname: 'timestamp', type: RecordeditInputType.TIMESTAMP, disabled: false, + value: { date_value: '2016-05-14', time_value: '17:30:00' } + }, + { + name: 'timestamp_disabled', displayname: 'timestamp_disabled', type: RecordeditInputType.TIMESTAMP, disabled: true, + value: { date_value: '2012-06-22', time_value: '18:36:00' } + }, + { + name: 'timestamp_disabled_no_default', displayname: 'timestamp_disabled_no_default', + type: RecordeditInputType.TIMESTAMP, disabled: true, autoGenerated: true + }, + + { + name: 'timestamptz', displayname: 'timestamptz', type: RecordeditInputType.TIMESTAMP, disabled: false, + value: { date_value: '2014-05-07', time_value: '14:40:00' } + }, + { + name: 'timestamptz_disabled', displayname: 'timestamptz_disabled', type: RecordeditInputType.TIMESTAMP, disabled: true, + value: { date_value: '2010-06-13', time_value: '17:22:00' } + }, + { + name: 'timestamptz_disabled_no_default', displayname: 'timestamptz_disabled_no_default', + type: RecordeditInputType.TIMESTAMP, disabled: true, autoGenerated: true + }, + + { + name: 'json', displayname: 'json', type: RecordeditInputType.JSON, disabled: false, + value: JSON.stringify({ 'name': 'testing_json' }) + }, + { + name: 'json_disabled', displayname: 'json_disabled', type: RecordeditInputType.JSON, disabled: true, + value: JSON.stringify(98.786) + }, + { + name: 'json_disabled_no_default', displayname: 'json_disabled_no_default', type: RecordeditInputType.JSON, + disabled: true, autoGenerated: true + }, + + { name: 'color_rgb_hex', displayname: 'color_rgb_hex', type: RecordeditInputType.COLOR, value: '#123456', disabled: false }, + { name: 'color_rgb_hex_disabled', displayname: 'text_disabled', type: RecordeditInputType.COLOR, value: '#654321', disabled: true }, + + { name: 'RID', displayname: 'RID', type: RecordeditInputType.TEXT, disabled: true, autoGenerated: true }, + { name: 'RCB ', displayname: 'RCB ', type: RecordeditInputType.TEXT, disabled: true, autoGenerated: true }, + { name: 'RMB ', displayname: 'RMB ', type: RecordeditInputType.TEXT, disabled: true, autoGenerated: true }, + { name: 'RCT', displayname: 'RCT', type: RecordeditInputType.TIMESTAMP, disabled: true, autoGenerated: true }, + { name: 'RMT', displayname: 'RMT', type: RecordeditInputType.TIMESTAMP, disabled: true, autoGenerated: true }, + ], + submission: { + tableDisplayname: 'defaults-table', + resultColumnNames: [ + 'text', 'text_disabled', 'markdown', 'markdown_disabled', + 'foreign_key', 'foreign_key_dropdown', 'foreign_key_disabled', + 'int', 'int_disabled', 'float', 'float_disabled', 'boolean_true', 'boolean_false', 'boolean_disabled', + 'date', 'date_disabled', 'timestamp', 'timestamp_disabled', 'timestamptz', 'timestamptz_disabled', + 'json', 'json_disabled', 'color_rgb_hex', 'color_rgb_hex_disabled' + ], + resultRowValues: [[ + 'default', 'Disabled input', 'bold', 'italics', + { caption: 'Default for foreign_key column', url: '/defaults:foreign-text-table/' }, + { caption: 'Default for foreign_key_dropdown column', url: '/defaults:foreign-dropdown-table/' }, + { caption: 'Default for foreign_key_disabled column', url: '/defaults:foreign-text-disabled-table/' }, + '25', '20', '1.6478', '93.2182', 'true', 'false', 'false', '2010-06-08', '2014-05-12', '2016-05-14 17:30:00', + '2012-06-22 18:36:00', '2014-05-07 14:40:00', '2010-06-13 17:22:00', JSON.stringify({ 'name': 'testing_json' }, undefined, 2), + JSON.stringify(98.786, undefined, 2), '#123456', '#654321' + ]] + } + }, + edit: { + columns: [ + { name: 'asset_disabled', displayname: 'asset_disabled', type: RecordeditInputType.FILE, value: '28110_191_z.jpg', disabled: false }, + { name: 'text_disabled', displayname: 'text_disabled', type: RecordeditInputType.TEXT, value: 'Disabled input' }, + { name: 'markdown_disabled', displayname: 'markdown_disabled', type: RecordeditInputType.MARKDOWN, value: '*italics*', disabled: true }, + { + name: 'WnsyE4pJ1O0IW8zsj6MDHg', displayname: 'foreign_key_disabled', type: RecordeditInputType.FK_POPUP, + value: 'Default for foreign_key_disabled column', disabled: true + }, + { name: 'int_disabled', displayname: 'int_disabled', type: RecordeditInputType.INT_4, value: '20', disabled: true }, + { name: 'float_disabled', displayname: 'int_disabled', type: RecordeditInputType.NUMBER, value: '93.2182', disabled: true }, + { name: 'boolean_disabled', displayname: 'boolean_disabled', type: RecordeditInputType.BOOLEAN, value: 'false', disabled: true }, + { name: 'date_disabled', displayname: 'date_disabled', type: RecordeditInputType.DATE, value: '2014-05-12', disabled: true }, + { + name: 'timestamp_disabled', displayname: 'timestamp_disabled', type: RecordeditInputType.TIMESTAMP, disabled: true, + value: { date_value: '2012-06-22', time_value: '18:36:00' } + }, + { + name: 'timestamptz_disabled', displayname: 'timestamptz_disabled', type: RecordeditInputType.TIMESTAMP, disabled: true, + value: { date_value: '2010-06-13', time_value: '17:22:00' } + }, + { + name: 'json_disabled', displayname: 'json_disabled', type: RecordeditInputType.JSON, disabled: true, + value: JSON.stringify(98.786) + }, + { name: 'color_rgb_hex_disabled', displayname: 'text_disabled', type: RecordeditInputType.COLOR, value: '#654321', disabled: true }, + ] + } + +} + +test.describe('Immutable and Generated columns', () => { + test.describe.configure({ mode: 'parallel' }); + + test('Create mode', async ({ page, baseURL }, testInfo) => { + await test.step('open recordedit page', async () => { + await page.goto(`${baseURL}/recordedit/#${getCatalogID(testInfo.project.name)}/${testParams.schemaTable}`); + await RecordeditLocators.waitForRecordeditPageReady(page); + }); + + for await (const col of testParams.create.columns) { + await test.step(`${col.displayname}`, async () => { + await test.step(`display ${col.disabled ? 'disabled' : ''} input with proper value`, async () => { + await testInputValue(page, 1, col.name, col.displayname, col.type, col.disabled, col.value); + }); + + if (col.autoGenerated) { + await test.step('display the "Automatically generated" placeholder', async () => { + switch (col.type) { + case RecordeditInputType.TIMESTAMP: + const props = RecordeditLocators.getTimestampInputsForAColumn(page, col.name, 1); + await testAutomaticallyGenerated(props.time, col.displayname); + await testAutomaticallyGenerated(props.date, col.displayname); + break; + case RecordeditInputType.FILE: + case RecordeditInputType.FK_DROPDOWN: + case RecordeditInputType.FK_POPUP: + case RecordeditInputType.BOOLEAN: + await expect.soft(RecordeditLocators.getInputPlaceholderMessage(page, col.name, 1)).toHaveText('Automatically generated'); + // TODO + break; + default: + await testAutomaticallyGenerated(RecordeditLocators.getInputForAColumn(page, col.name, 1), col.displayname); + break; + } + }); + } + }) + } + + await test.step('submit and save the data', async () => { + await testSubmission(page, testParams.create.submission); + }); + }); + + test('Edit mode', async ({ page, baseURL }, testInfo) => { + await test.step('open recordedit page', async () => { + await page.goto(`${baseURL}/recordedit/#${getCatalogID(testInfo.project.name)}/${testParams.schemaTable}/id=2`); + await RecordeditLocators.waitForRecordeditPageReady(page); + await page.pause(); + }); + + await test.step('display immutable inputs properly with their values.', async () => { + for await (const col of testParams.edit.columns) { + await test.step(`${col.displayname}`, async () => { + await testInputValue(page, 1, col.name, col.displayname, col.type, true, col.value); + }); + } + }); + }); + +}) + +const testAutomaticallyGenerated = async (inp: Locator, displayname: string) => { + await expect.soft(inp, displayname).toHaveAttribute('placeholder', 'Automatically generated'); +} + diff --git a/test/e2e/specs/default-config/recordedit/submission-disabled.conf.js b/test/e2e/specs/default-config/recordedit/submission-disabled.conf.js deleted file mode 100644 index f8bde8521..000000000 --- a/test/e2e/specs/default-config/recordedit/submission-disabled.conf.js +++ /dev/null @@ -1,15 +0,0 @@ -var pConfig = require('./../../../utils/protractor.configuration.js'); - -var config = pConfig.getConfig({ - configFileName: 'recordedit/submission-disabled.dev.json', - chaiseConfigFilePath: 'test/e2e/specs/default-config/chaise-config.js', - specs: [ - "submission-disabled.spec.js" - ], - setBaseUrl: function(browser, data) { - browser.params.url = process.env.CHAISE_BASE_URL; - return browser.params.url; - } -}); - -exports.config = config; diff --git a/test/e2e/specs/default-config/recordedit/submission-disabled.spec.js b/test/e2e/specs/default-config/recordedit/submission-disabled.spec.js deleted file mode 100644 index 91e2c5935..000000000 --- a/test/e2e/specs/default-config/recordedit/submission-disabled.spec.js +++ /dev/null @@ -1,71 +0,0 @@ -var chaisePage = require('../../../utils/chaise.page.js'); -var testParams = { - table_name: "submission-disabled-table", - key: { - name: "id", - value: "1000", - operator: "=" - }, - conflict_table_name: "duplicate_key_conflict", - conflict_column: "duplicate_id", - conflict_key: "1000", - conflict_message: "The entry cannot be created/updated. Please use a different duplicate_id for this record. Click here to see the conflicting record that already exists." -}; - -describe("For error handling strategies on submission,", function() { - - describe("when editing a record without changing data,", function() { - - beforeAll(function () { - var keys = []; - keys.push(testParams.key.name + testParams.key.operator + testParams.key.value); - - chaisePage.navigate(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/submission-disabled:" + testParams.table_name + "/" + keys.join("&")); - - chaisePage.recordeditPageReady(); - }); - - // NOTE: adding this test here because eventually we are going to change the behavior - // of updating an entity with no changes. Currently the update function throws an error, - // whereas, later we will be disabling the submission button until changes were made in the form - it("warn the user when submitting data when no data was changed.", function(done) { - chaisePage.recordEditPage.submitForm().then(() => { - const alert = chaisePage.recordEditPage.getAlertWarning(); - return chaisePage.waitForElement(alert); - }).then(() => { - done(); - expect(alert.getText()).toEqual("Warning No data was changed in the update request. Please check the form content and resubmit the data."); - }).catch(chaisePage.catchTestError(done)); - }); - }); - - describe("when creating a record,", function() { - - var uri; - - beforeAll(function () { - uri = browser.params.url + "/recordedit/#" + browser.params.catalogId + "/submission-disabled:" + testParams.conflict_table_name; - chaisePage.navigate(uri); - - chaisePage.recordeditPageReady(); - }); - - it("a conflict error should show an alert with a link to the conflicting record.", function(done) { - const idInput = chaisePage.recordEditPage.getInputForAColumn(testParams.conflict_column, 1); - - idInput.sendKeys(testParams.conflict_key); - expect(idInput.getAttribute('value')).toBe(testParams.conflict_key, "input does not show the correct value"); - - chaisePage.recordEditPage.submitForm().then(function () { - return browser.wait(function() { return chaisePage.recordEditPage.getAlertError(); }, browser.params.defaultTimeout); - }).then(function(alert) { - expect(alert.getText()).toContain(testParams.conflict_message, "alert message is incorrect"); - - var duplicate_uri = uri.replace('recordedit', 'record') + '/' + testParams.conflict_column + '=' + testParams.conflict_key; - expect(chaisePage.recordEditPage.getAlertErrorLink().getAttribute('href')).toContain(duplicate_uri, "link to duplicate record is incorrect"); - - done(); - }).catch(chaisePage.catchTestError(done)); - }); - }); -}); diff --git a/test/e2e/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts b/test/e2e/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts index 493a7df8d..f6c8fa284 100644 --- a/test/e2e/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts +++ b/test/e2e/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts @@ -34,7 +34,6 @@ test.describe('Navbar with chaise-config annotation', () => { test('should hide the navbar bar if the hideNavbar query parameter is set to true', async ({ page, baseURL }, testInfo) => { const PAGE_URL = `/recordset/#${getCatalogID(testInfo.project.name)}/catalog-config-navbar:config-table`; - await page.pause(); await page.goto(`${baseURL}${PAGE_URL}?hideNavbar=true`); await RecordsetLocators.waitForRecordsetPageReady(page); await expect.soft(NavbarLocators.getContainer(page)).not.toBeAttached(); diff --git a/test/e2e/specs/delete-prohibited/recordset/histogram-facet.spec.ts b/test/e2e/specs/delete-prohibited/recordset/histogram-facet.spec.ts index 1cf50041e..0b9a6c652 100644 --- a/test/e2e/specs/delete-prohibited/recordset/histogram-facet.spec.ts +++ b/test/e2e/specs/delete-prohibited/recordset/histogram-facet.spec.ts @@ -63,7 +63,8 @@ const testParams = { }; test.describe('Testing features for range picker facet types with histograms', () => { - test.describe.configure({ mode: 'parallel' }); + // this test is flaky, so we're going to retry it if it failed the first time. + test.describe.configure({ mode: 'parallel', retries: 3 }); for (const [index, facetParams] of testParams.facets.entries()) { test(`Testing facet: ${facetParams.name}`, async ({ page, baseURL }, testInfo) => { diff --git a/test/e2e/utils/chaise.page.js b/test/e2e/utils/chaise.page.js deleted file mode 100644 index 457ea2afb..000000000 --- a/test/e2e/utils/chaise.page.js +++ /dev/null @@ -1,1714 +0,0 @@ -const { element, browser } = require('protractor'); -var Q = require('q'); - -var recordEditPage = function() { - var that = this; - - // recordedit form view - - // TODO playwright: renamed to getPageTitle - this.getEntityTitleElement = function() { - return element(by.css('.app-container #page-title')); - }; - - // TODO playwright: renamed to getPageTitleLink - this.getEntityTitleLinkElement = function() { - return this.getEntityTitleElement().element(by.tagName('a')); - }; - - this.getRequiredInfoEl = () => { - return element(by.className('required-info')); - } - - this.getAllColumnNames = function() { - return element.all(by.css(".entity-key-column > .entity-key > span.column-displayname > span")); - }; - - this.getAllColumnPermissionOverlays = function () { - return element.all(by.css(".column-permission-overlay")); - } - - this.getColumnPermissionOverlay = function (formNumber, displayName) { - displayName = makeSafeIdAttr(displayName); - return element(by.css(".column-permission-overlay-" + formNumber + '-' + displayName)); - } - - this.getColumnPermissionError = function (formNumber, displayName) { - displayName = makeSafeIdAttr(displayName); - return element(by.css(".column-permission-warning-" + formNumber + '-' + displayName)); - }; - - this.getColumnsWithUnderline = function() { - return element.all(by.css('.entity-key-column > .entity-key > span.column-displayname.chaise-icon-for-tooltip')); - }; - - this.getColumnInlineComments = () => { - return element.all(by.css('.inline-comment-row')); - }; - - this.getColumnWithAsterisk = function(el) { - return el.element(by.xpath('./../..')).element(by.className('text-danger')); - }; - - this.getRecordeditForms = () => { - return element.all(by.css('.recordedit-form .form-header')) - } - - this.getCloneFormInput = function () { - return element(by.id("copy-rows-input")); - }; - - this.getCloneFormInputSubmitButton = function () { - return element(by.id("copy-rows-submit")); - }; - - this.getRecordeditResetButton = function () { - return element(by.id('recordedit-reset')); - }; - - this.getSubmitRecordButton = function () { - return element(by.id("submit-record-button")); - }; - - this.getBulkDeleteButton = function () { - return element(by.id('bulk-delete-button')); - }; - - /* input selectors */ - this.getInputRemoveButton = function (name, index) { - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)).element(by.css('.remove-input-btn')); - } - - this.clearInput = function(el) { - return el.getAttribute('value').then(function(value) { - el.sendKeys(Array(value.length + 1).join(protractor.Key.BACK_SPACE)); - browser.sleep(10); - }).catch(function (err) { - console.log(err) - }); - }; - - this.getInputForAColumn = function(name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className(inputName)); - }; - - this.getInputById = function (index, displayName) { - displayName = makeSafeIdAttr(displayName); - return element(by.id("form-" + index + '-' + displayName + "-input")); - }; - - // NOTE: duplicate of getInputForAColumn, not removing this and refactoring tests since protractor tests will be deprecated soon - this.getTextAreaForAColumn = function(name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className(inputName)); - }; - - this.getDateInputsForAColumn = function(name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - const inputObj = {}; - // NOTE: duplicate of getInputForAColumn, not changing this since protractor tests will be deprecated soon - inputObj.date = element(by.className(inputName)); - inputObj.todayBtn = element(by.css(`.input-switch-container-${inputName} .date-today-btn`)); - return inputObj; - }; - - this.getTimestampInputsForAColumn = function(name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - var inputObj = {}; - const container = element(by.className(`input-switch-container-${inputName}`)); - inputObj.date = element(by.className(`${inputName}-date`)); - inputObj.time = element(by.className(`${inputName}-time`)); - inputObj.nowBtn = container.element(by.css('.date-time-now-btn')); - inputObj.clearBtn = container.element(by.css('.date-time-clear-btn')); - return inputObj; - }; - - this.getInputControlForAColumn = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)).element(by.css('.chaise-input-control')); - } - - this.getTextFileInputForAColumn = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)).element(by.css('.chaise-input-control > span')); - } - - /* Color input selectors */ - this.getColorInputForAColumn = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)).element(by.tagName('input')); - } - - this.getColorInputBackground = function (name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - var script = "var color = document.querySelector('.input-switch-container-" + inputName + " .chaise-color-picker-preview').style.backgroundColor;"; - script += "var ctx = document.createElement('canvas').getContext('2d');ctx.fillStyle = color;"; - script += "return ctx.fillStyle;"; - return browser.executeScript(script); - }; - - this.getColorInputBtn = function (name, index) { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.css('.input-switch-container-' + inputName + ' button')); - } - - this.getColorInputPopup = function () { - return element(by.css('.chaise-color-picker-popup:not(.sp-hidden)')); - }; - - this.getColorInputPopupInput = function () { - return this.getColorInputPopup().element(by.css('.sp-input')); - }; - - this.getColorInputPopupClearBtn = function () { - return this.getColorInputPopup().element(by.css('.sp-clear')); - }; - - this.getColorInputPopupSelectBtn = function () { - return this.getColorInputPopup().element(by.css('.sp-choose')); - }; - - /* Multi form input selectors */ - this.getMultiFormToggleButton = function (name) { - var columnDisplayName = makeSafeIdAttr(name); - return element(by.css('.multi-form-' + columnDisplayName)); - }; - - this.getMultiFormApplyBtn = function () { - return element(by.css('.multi-form-input-apply-btn')); - } - - this.getMultiFormClearBtn = function () { - return element(by.css('.multi-form-input-clear-btn')); - } - - this.getMultiFormCloseBtn = function () { - return element(by.css('.multi-form-input-close-btn')); - } - - this.getMultiFormInputCheckbox = function () { - return element(by.css('.multi-form-input-checkbox input')); - } - - this.getMultiFormInputCheckboxLabel = function () { - return element(by.css('.multi-form-input-checkbox label')); - } - - /* dropdown selectors */ - this.getDropdownElementByName = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.css('.input-switch-container-' + inputName + ' .dropdown-toggle')); - } - - // foreign key dropdown selectors - this.getFkeyDropdowns = () => { - return element.all(by.css('.fk-dropdown')); - } - - this.getDropdownSelectableOptions = () => { - return element(by.css('.dropdown-menu.show')).all(by.css('.dropdown-select-value')); - } - - this.getDropdownLoadMoreRow = () => { - return element(by.css('.dropdown-menu .load-more-row')); - } - - this.getDropdownSearch = () => { - return element(by.css('.dropdown-menu .search-row .chaise-input-control input')); - } - - // boolean dropdown selectors - this.getDropdownText = (el) => { - return el.element(by.css('.chaise-input-control')); - }; - - this.getOpenDropdownOptionsContainer = () => { - return element(by.css(".dropdown-menu.show")); - } - - // Gets the boolean dropdown options after the input is opened and attached to input container - this.getDropdownOptions = function() { - return element(by.css(".dropdown-menu.show")).all(by.tagName('li')); - } - - this.selectDropdownValue = function(dropdownEl, value) { - var self = this; - return dropdownEl.getText().then(function(txt) { - var defer = Q.defer(); - // if the existing selection isn't the desired value, - if (txt.trim() !== value) { - // Click open the dropdown - dropdownEl.click().then(function () { - const optionsContainer = self.getOpenDropdownOptionsContainer(); - return browser.wait(protractor.ExpectedConditions.visibilityOf(optionsContainer), browser.params.defaultTimeout); - }).then(() => { - // Get all the possible choices in the dropdown - return self.getDropdownOptions(); - }).then(function (options) { - // loop through options and check for one that matches our value we want to click - options.forEach(function (option, index) { - option.getAttribute("innerHTML").then(function (optionTxt) { - if (optionTxt.trim() == value + "") { - try { - option.click() - } catch (e) {} - defer.resolve(); - } else if (index == options.length-1) { - // we didn't match any and this is the last one - defer.reject(); - } - }); - }); - }); - - } else { - defer.resolve(txt); - } - return defer.promise; - }); - }; - - /* Foreign key input and popup selectors */ - this.getModalPopupBtns = function() { - return element.all(by.css(".modal-popup-btn")); - }; - - this.getModalTitle = function() { - return element(by.css(".modal-title")); - }; - - this.getModalActionBody = function() { - return element(by.css('.modal-body')).all(by.tagName('span')).get(1); - }; - - this.getModalCloseBtn = function() { - return element(by.css(".modal-close")); - }; - - this.getForeignKeyInputDisplay = function(columnDisplayName, index) { - columnDisplayName = makeSafeIdAttr(columnDisplayName); - return element(by.id("form-" + index + '-' + columnDisplayName + "-display")); - }; - - this.getForeignKeyInputButton = function(columnDisplayName, index) { - columnDisplayName = makeSafeIdAttr(columnDisplayName); - return element(by.id("form-" + index + '-' + columnDisplayName + "-button")); - }; - - this.getForeignKeyInputClear = function(columnDisplayName, index) { - return this.getForeignKeyInputDisplay(columnDisplayName, index).element(by.className('remove-input-btn')); - }; - - this.getForeignKeyInputs = function() { - return element.all(by.css(".popup-select-value")); - }; - - /** - * returns the cell (entity-value). - * this is useful if we want to test the extra classes attached to it. - */ - this.getFormInputCell = (name, index, isArray) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - - if(isArray){ - return element(by.className('array-input-field-container ' + inputName)).element(by.xpath('..')); - } - return element(by.className('input-switch-container-' + inputName)).element(by.xpath('..')); - }; - - this.getInputSwitchContainer = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)) - } - // TODO: This is BAD, no column name should be hardcoded - this.getInputSwitchContainerFK = (index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + 'lIHKX0WnQgN1kJOKR0fK5A'; - return element(by.className('input-switch-container-' + inputName)) - } - - this.getIframeInputDisplay = (container, name, index) => { - if (!container) { - container = this.getInputSwitchContainer(name, index); - } - return container.element(by.css('.chaise-input-control')); - }; - - this.getIframeInputButton = (container, name, index) => { - if (!container) { - container = this.getInputSwitchContainer(name, index); - } - return container.element(by.css('.chaise-input-group-append button')); - }; - - this.getIframeInputClear = (container, name, index) => { - if (!container) { - container = this.getInputSwitchContainer(name, index); - } - return container.element(by.css('.input-switch-clear')); - }; - - this.getIframeInputPopupSpinner = () => { - return element(by.className('iframe-field-modal-spinner')); - }; - - this.getIframeInputPopupIframe = () => { - return element(by.css('.iframe-field-popup iframe')); - }; - - this.getIframeInputPopupSubmitBtn = () => { - return element(by.id('iframe-submit-btn')); - }; - - this.getIframeInputPopupAlertBtn = () => { - return element(by.id('iframe-alert-btn')); - }; - - this.getIframeInputPopupInputs = () => { - return { - creator: element(by.id('creator')), - file_content: element(by.id('file-content')), - notes: element(by.id('notes')) - } - } - - this.getIframeInputCancelPopup = () => { - const modal = element(by.css('.confirm-iframe-close-modal')); - return { - element: modal, - body: modal.element(by.css('.modal-body')), - cancelButton: modal.element(by.css('.cancel-button')) - }; - } - - this.submitForm = function() { - const defer = Q.defer(); - - element(by.css(('button[type="submit"]'))).click().then(() => { - defer.resolve(); - }); - - return defer.promise; - }; - - /* error message selectors */ - this.getErrorMessageForAColumn = (name, index) => { - index = index || 1; - const inputName = 'c_' + index + '-' + name; - return element(by.className('input-switch-container-' + inputName)).element(by.css('.input-switch-error')); - } - - this.getInputErrorMessage = function(el, type) { - return el.element(by.xpath('./../..')).element(by.css('.input-switch-error')); - }; - - this.getJSONInputErrorMessage = function(el) { - // similar input structure as array detailed below - return el.element(by.xpath('./../../..')).element(by.css('.input-switch-error')); - }; - - this.getArrayInputErrorMessage = function(el) { - /** - * The error message is a sibling of the grandparent to the textarea. Get the great grandparent and select the error from there - *
- *
- *
- *