From 293f7230e05d1e91e8d518013a1a98e6e8061967 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Mon, 2 Nov 2020 12:07:46 +0100 Subject: [PATCH 01/11] Update cmds and paths in the readme --- scala3doc/README.md | 61 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/scala3doc/README.md b/scala3doc/README.md index bdc7a6487751..5f065c3f70cb 100644 --- a/scala3doc/README.md +++ b/scala3doc/README.md @@ -17,11 +17,18 @@ We're aiming to support all the features Scaladoc did, plus new and exciting one ## Running the project -Use the following commands to generate documentation for this project and for Dotty, respectively: +Run `sbt` and switch to scala3doc project from the root ``` -sbt generateSelfDocumentation -sbt generateDottyLibDocumentation +sbt +project scala3doc +``` + +When in the `scala3doc` project in `sbt`, use the following commands to generate documentation for this project and for Dotty, respectively: + +``` +generateSelfDocumentation +generateScala3Documentation ``` To actually view the documentation, the easiest way is to run the following in project root: @@ -72,36 +79,22 @@ You can also find the result of building the same sites for latest `master` at: ### Testing -To implement integration tests that inspects state of the model on different stages of -documentation generation one can use [ScaladocTest](src/test/scala/dotty/dokka/ScaladocTest.scala#L15). -This class requires providing the name of the test and a the list of assertions. - -Name of the test is used to extract symbols that should be included in given test run from -the TASTY files. All test data is located in [scala3doc-testcases module](../scala3doc-testcases/src). -It is compiled together with the rest of modules. This solves lots of potential problems with -invoking the compiler in a separate process such as mismatching versions. Every test is using -only symbols defined in the package with the same name as the test located inside top level `tests` -package (including subpackages). This restriction may be relaxed in the future. +Most tests rely on comparing signatures (of classes, methods, objects etc.) extracted from the generated documentation +to signatures found in source files. Such tests are defined using [MultipleFileTest](test/dotty/dokka/MultipleFileTest.scala) class +and its subtypes (such as [SingleFileTest](test/dotty/dokka/SingleFileTest.scala)) -Assertions of each test are defined as list of [Assertion enum](src/test/scala/dotty/dokka/ScaladocTest.scala#L63) -instances. Each of them represents a callback that is invoked in corresponding phase of -documentation generation. Those callback can inspect the model, log information and raise errors -using `reportError` method. +WARNING: As the classes mentioned above are likely to evolve, the description below might easily get out of date. +In case of any discrepancies rely on the source files instead. -#### Signature tests - -Some of the tests rely on comparing signatures (of classes, methods, objects etc.) extracted from -the generated documentation to signatures found in source files. Such tests are defined using -[SignatureTest](src/test/scala/dotty/dokka/SignatureTest.scala#L16) class, that is a subclass of -`ScaladocTest` and uses exactly tha same mechanism to find input symbols in the TASTY files. - -Signature tests by default assume that sources that were used to generate used TASTY files are -located in the file with the same name as the name of the test. If this is not the case optional -parameter `sourceFiles` can be used to pass list of source file names (without `.scala` extension). - -Those tests also requires specifying kinds of ignatures from source files (corresponding to -keywords used to declare them like `def`, `class`, `object` etc.) whose presence in the generated -documentation will be checked (other signatures, when missing, will be ignored). +`MultipleFileTest` requires that you specify the names of the files used to extract signatures, +the names of directories containing corresponding TASTY files +and the kinds of signatures from source files (corresponding to keywords used to declare them like `def`, `class`, `object` etc.) +whose presence in the generated documentation will be checked (other signatures, when missing, will be ignored). +The mentioned source files should be located directly inside `src/main/scala/tests` directory +but the file names passed as parameters should contain neither this path prefix nor `.scala` suffix. +The TASTY folders are expected to be located in `target/${dottyVersion}/classes/tests` (after successful compilation of the project) +and similarly only their names relative to this path should be provided as tests' parameters. +For `SingleFileTest` the name of the source file and the TASTY folder are expected to be the same. By default it's expected that all signatures from the source files will be present in the documentation but not vice versa (because the documentation can contain also inherited signatures). @@ -129,7 +122,6 @@ instead of def foo(): Int ``` - Because of the way how signatures in source are parsed, they're expected to span until the end of a line (including comments except those special ones mentioned above, which change the behaviour of tests) so if a definition contains an implementation, it should be placed in a separate line, e.g. ``` @@ -150,8 +142,9 @@ Otherwise the implementation would be treated as a part of the signature. 1. Replace Dottydoc as the dedicated tool for documenting Dotty code This includes: - + supporting Dotty's doc pages - + releasing together with Dotty as the dedicated documentation tool + + - supporting Dotty's doc pages + - releasing together with Dotty as the dedicated documentation tool 1. Support all kinds of Dotty definition and generate documentation for the standard library From 8798ae40fd2210bea65486a9fcabce3af8e6a0f3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Mon, 2 Nov 2020 16:06:23 +0100 Subject: [PATCH 02/11] Add jsdoc type annotations for Filter --- .../dotty_res/scripts/common/component.js | 1 - .../dotty_res/scripts/components/Filter.js | 74 +++++++++++++++++-- .../dotty_res/scripts/components/FilterBar.js | 4 + .../scripts/components/FilterGroup.js | 1 + 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/common/component.js b/scala3doc/resources/dotty_res/scripts/common/component.js index 085aec3fe42d..de4a743cefb5 100644 --- a/scala3doc/resources/dotty_res/scripts/common/component.js +++ b/scala3doc/resources/dotty_res/scripts/common/component.js @@ -6,7 +6,6 @@ class Component { } setState(nextState, cb = () => {}) { - const prevState = { ...this.state }; if (typeof nextState === "function") { this.state = { ...this.state, diff --git a/scala3doc/resources/dotty_res/scripts/components/Filter.js b/scala3doc/resources/dotty_res/scripts/components/Filter.js index beb89c5fdbea..7231b158724d 100644 --- a/scala3doc/resources/dotty_res/scripts/components/Filter.js +++ b/scala3doc/resources/dotty_res/scripts/components/Filter.js @@ -2,7 +2,22 @@ const defaultFilterGroup = { FOrdering: { Alphabetical: true }, }; +/** + * @typedef { Object } FilterItem + * @prop { string } selected + * @prop { number } visible + */ + + /** + * @typedef { Record } Filters + */ + class Filter { + /** + * @param value { string } + * @param filters { Filters } + * @param elementsRefs { Element } + */ constructor(value, filters, elementsRefs, init = false) { this._init = init; this._value = value; @@ -23,6 +38,10 @@ class Filter { return this._elementsRefs; } + /** + * @param key { string } + * @param value { string } + */ onFilterToggle(key, value) { return new Filter( this.value, @@ -31,6 +50,10 @@ class Filter { ); } + /** + * @param key { string } + * @param isActive { boolean } + */ onGroupSelectionChange(key, isActive) { return new Filter( this.value, @@ -39,6 +62,10 @@ class Filter { ); } + /** + * @param value { string } + * @returns { FilterItem } + */ onInputValueChange(value) { return new Filter( value, @@ -47,6 +74,11 @@ class Filter { ); } + /** + * @private + * @param value { string } + * @returns { Filters } + */ _generateFiltersOnTyping(value) { return this.elementsRefs .filter((elRef) => { @@ -72,6 +104,10 @@ class Filter { }, this._allFiltersAreHidden()); } + /** + * @private + * @returns { Filters } + */ _allFiltersAreHidden() { return Object.entries(this.filters).reduce( (filters, [key, filterGroup]) => { @@ -87,6 +123,12 @@ class Filter { ); } + /** + * @private + * @param key { string } + * @param isActive { boolean } + * @returns { Filters } + */ _withNewSelectionOfGroup(key, isActive) { return { ...this.filters, @@ -103,8 +145,13 @@ class Filter { }; } + /** + * @private + * @returns { Filters } + */ _withNewFilters() { - return this._elementsRefs.reduce((filtersObject, elementRef) => { + console.log("this._elementsRefs", this._elementsRefs) + const newFilters = this._elementsRefs.reduce((filtersObject, elementRef) => { this._getDatasetWithF(elementRef.dataset).map(([key, value]) => this._splitByComma(value).map((val) => { if (!filtersObject[key]) { @@ -112,18 +159,23 @@ class Filter { } else { filtersObject[key] = { ...filtersObject[key], - [val]: filtersObject[key][val] ?? { - selected: true, - visible: true, - }, + [val]: filtersObject[key][val] ?? { selected: true, visible: true }, }; } }) ); return filtersObject; }, {}); + console.log("newFilters", newFilters) + return newFilters } + /** + * @private + * @param key { string } + * @param value { string } + * @returns { Filters } + */ _withToggledFilter(key, value) { return { ...this.filters, @@ -137,10 +189,16 @@ class Filter { }; } + /** + * @private + * @param str { string } + */ _splitByComma = (str) => str.split(","); + /** + * @private + * @param dataset { DOMStringMap } + */ _getDatasetWithF = (dataset) => - Object.entries(dataset).filter(([key]) => this._startsWithF(key)); - - _startsWithF = (str) => startsWith(str, "f"); + Object.entries(dataset).filter(([key]) => startsWith(key, "f")); } diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterBar.js b/scala3doc/resources/dotty_res/scripts/components/FilterBar.js index c030b372987d..b1fc92031b8d 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterBar.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterBar.js @@ -1,3 +1,7 @@ +/** + * @typedef { import("./Filter").Filter } Filter + */ + class FilterBar extends Component { constructor(props) { super(props); diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js index ad28bfee1e00..6065c582322d 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js @@ -99,6 +99,7 @@ class FilterGroup extends Component { } render({ filter }) { + console.log(filter.filters) attachDOM( this.filterLowerContainerRef, Object.entries(filter.filters) From e8f1ea54a8dbb750967611533104c4adce017934 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Mon, 2 Nov 2020 17:39:29 +0100 Subject: [PATCH 03/11] Add jsdoc type annotations for List --- .../scripts/components/DocumentableList.js | 138 +++++++++--------- .../dotty_res/scripts/components/Filter.js | 50 +++---- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js index 138681402d08..994f937e2085 100644 --- a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js +++ b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js @@ -1,3 +1,7 @@ +/** + * @typedef { import("./Filter").Filter } Filter + */ + class DocumentableList extends Component { constructor(props) { super(props); @@ -14,24 +18,16 @@ class DocumentableList extends Component { this.render(this.props); } - toggleElementDatasetVisibility(condition, element) { - if (condition) { - element.dataset.visibility = true; - } else { - element.dataset.visibility = false; - } + toggleElementDatasetVisibility(isVisible, ref) { + ref.dataset.visibility = isVisible } toggleDisplayStyles(condition, ref, onVisibleStyle) { - if (condition) { - ref.style.display = onVisibleStyle; - } else { - ref.style.display = "none"; - } + ref.style.display = condition ? onVisibleStyle : 'none' } render({ filter }) { - this.state.list.sectionsRefs.map((sectionRef) => { + this.state.list.sectionsRefs.map(sectionRef => { const tabRef = this.state.list.getTabRefFromSectionRef(sectionRef); const isTabVisible = this.state.list @@ -39,22 +35,13 @@ class DocumentableList extends Component { .filter((listRef) => { const isListVisible = this.state.list .getSectionListElementsRefs(listRef) - .map((elementRef) => this.state.list.mapListElementRef(elementRef)) - .filter((elementData) => { - const isElementVisible = this.state.list.isElementVisible( - elementData, - filter - ); - - this.toggleDisplayStyles( - isElementVisible, - elementData.ref, - "table" - ); - this.toggleElementDatasetVisibility( - isElementVisible, - elementData.ref - ); + .map(elementRef => this.state.list.toListElement(elementRef)) + .filter(elementData => { + const isElementVisible = this.state.list.isElementVisible(elementData, filter); + + this.toggleDisplayStyles(isElementVisible, elementData.ref, "table"); + this.toggleElementDatasetVisibility(isElementVisible, elementData.ref); + return isElementVisible; }).length; @@ -68,39 +55,65 @@ class DocumentableList extends Component { } } -class List { - filterTab(name) { - return name !== "Linear supertypes" && name !== "Known subtypes" && name !== "Type hierarchy" - } +/** + * @typedef { { ref: Element; name: string; description: string } } ListElement + */ +class List { + /** + * @param tabsRef { Element[] } + * @param sectionRefs { Element[] } + */ constructor(tabsRef, sectionRefs) { this._tabsRef = tabsRef; this._sectionRefs = sectionRefs; } get tabsRefs() { - return this._tabsRef.filter((tabRef) => this.filterTab(this._getTogglable(tabRef))); + return this._tabsRef.filter(tabRef => this.filterTab(this._getTogglable(tabRef))); } get sectionsRefs() { - return this._sectionRefs.filter( (sectionRef) => this.filterTab(this._getTogglable(sectionRef))); + return this._sectionRefs.filter(sectionRef => this.filterTab(this._getTogglable(sectionRef))); } + /** + * @param name { string } + */ + filterTab(name) { + return name !== "Linear supertypes" && name !== "Known subtypes" && name !== "Type hierarchy" + } + + /** + * @param sectionRef { Element } + */ getTabRefFromSectionRef(sectionRef) { return this.tabsRefs.find( (tabRef) => this._getTogglable(tabRef) === this._getTogglable(sectionRef) ); } + /** + * @param sectionRef { Element } + * @returns { Element[] } + */ getSectionListRefs(sectionRef) { return findRefs(".documentableList", sectionRef); } + /** + * @param listRef { Element } + * @returns { Element[] } + */ getSectionListElementsRefs(listRef) { return findRefs(".documentableElement", listRef); } - mapListElementRef(elementRef) { + /** + * @param elementRef { Element } + * @returns { ListElement } + */ + toListElement(elementRef) { return { ref: elementRef, name: getElementTextContent(getElementNameRef(elementRef)), @@ -108,43 +121,38 @@ class List { }; } + /** + * @param elementData { ListElement } + * @param filter { Filter } + */ isElementVisible(elementData, filter) { - if (!this._areFiltersFromElementSelected(elementData, filter)) { + if (!areFiltersFromElementSelected(elementData, filter)) { return false; + } else { + return includesInputValue(elementData, filter); } - return this._includesInputValue(elementData, filter); - } - _includesInputValue = (elementData, filter) => { - if (elementData.name.includes(filter.value)) { - return true; + function includesInputValue() { + return elementData.name.includes(filter.value) || elementData.description.includes(filter.value); } - return elementData.description.includes(filter.value); - }; - - _areFiltersFromElementSelected(elementRef, filter) { - const dataset = this._getCorrectDatasetFromElement(elementRef); - return dataset.length - ? this._hasCorrespodingFilters(dataset, filter.filters) - : true; - } - - _hasCorrespodingFilters = (dataset, filters) => - dataset.every(([key, value]) => { - const filterGroup = filters[key]; - return this._splitByComma(value).every( - (val) => filterGroup && filterGroup[val].selected - ); - }); - _getCorrectDatasetFromElement = (elementRef) => - Object.entries(elementRef.ref.dataset).filter(([key]) => - this._startsWithF(key) - ); + function areFiltersFromElementSelected() { + /** @type {[key: string, value: string][]} */ + const dataset = Object.entries(elementData.ref.dataset).filter(([key]) => startsWith(key, "f")); - _splitByComma = (str) => str.split(","); + const hasCorrespondingFilters = () => + dataset.every(([key, value]) => { + const filterGroup = filter.filters[key]; + return value.split(",").every(val => filterGroup && filterGroup[val].selected); + }); - _startsWithF = (str) => startsWith(str, "f"); + return dataset.length ? hasCorrespondingFilters() : true; + } + } - _getTogglable = (elementRef) => elementRef.dataset.togglable; + /** + * @param elementData { ListElement } + */ + _getTogglable = elementData => elementData.dataset.togglable; } + diff --git a/scala3doc/resources/dotty_res/scripts/components/Filter.js b/scala3doc/resources/dotty_res/scripts/components/Filter.js index 7231b158724d..ae256da33e66 100644 --- a/scala3doc/resources/dotty_res/scripts/components/Filter.js +++ b/scala3doc/resources/dotty_res/scripts/components/Filter.js @@ -1,22 +1,13 @@ -const defaultFilterGroup = { - FOrdering: { Alphabetical: true }, -}; - /** - * @typedef { Object } FilterItem - * @prop { string } selected - * @prop { number } visible - */ - - /** - * @typedef { Record } Filters + * @typedef { { selected: boolean; visible: boolean } } FilterItem + * @typedef { { fKeywords: Record } } Filters */ class Filter { /** * @param value { string } * @param filters { Filters } - * @param elementsRefs { Element } + * @param elementsRefs { Element[] } */ constructor(value, filters, elementsRefs, init = false) { this._init = init; @@ -87,10 +78,10 @@ class Filter { return name.includes(value) || description.includes(value); }) - .map((elRef) => this._getDatasetWithF(elRef.dataset)) + .map((elRef) => this._getDatasetWithKeywordData(elRef.dataset)) .reduce((filtersObject, datasets) => { - datasets.map(([key, value]) => { - this._splitByComma(value).map((val) => { + datasets.forEach(([key, value]) => { + this._splitByComma(value).forEach((val) => { filtersObject[key] = { ...filtersObject[key], [val]: { @@ -150,24 +141,24 @@ class Filter { * @returns { Filters } */ _withNewFilters() { - console.log("this._elementsRefs", this._elementsRefs) + const newFilter = { selected: true, visible: true } + const newFilters = this._elementsRefs.reduce((filtersObject, elementRef) => { - this._getDatasetWithF(elementRef.dataset).map(([key, value]) => - this._splitByComma(value).map((val) => { - if (!filtersObject[key]) { - filtersObject[key] = { [val]: { selected: true, visible: true } }; - } else { - filtersObject[key] = { - ...filtersObject[key], - [val]: filtersObject[key][val] ?? { selected: true, visible: true }, - }; - } + this._getDatasetWithKeywordData(elementRef.dataset).forEach(([key, value]) => + this._splitByComma(value).forEach((val) => { + filtersObject[key] = filtersObject[key] + ? { ...filtersObject[key], [val]: filtersObject[key][val] ?? newFilter} + : { [val]: newFilter } }) ); return filtersObject; }, {}); - console.log("newFilters", newFilters) - return newFilters + + const shouldAddDefaultFilter = this._elementsRefs.some(ref => !!ref.dataset['fKeywords']) + + return shouldAddDefaultFilter + ? { ...newFilters, fKeywords: { ...newFilters.fKeywords, default: newFilter } } + : newFilters } /** @@ -198,7 +189,8 @@ class Filter { /** * @private * @param dataset { DOMStringMap } + * @returns { [key: string, value: string][] } */ - _getDatasetWithF = (dataset) => + _getDatasetWithKeywordData = (dataset) => Object.entries(dataset).filter(([key]) => startsWith(key, "f")); } From 6495aea2170091d6624c3184ad6deb628a87b500 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Tue, 3 Nov 2020 10:28:16 +0100 Subject: [PATCH 04/11] Filter by default values in the FilterBar --- .../scripts/components/DocumentableList.js | 53 ++++++++++++------- .../dotty_res/scripts/components/Filter.js | 21 ++++---- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js index 994f937e2085..b4803aa06137 100644 --- a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js +++ b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js @@ -1,5 +1,6 @@ /** * @typedef { import("./Filter").Filter } Filter + * @typedef { { ref: Element; name: string; description: string } } ListElement */ class DocumentableList extends Component { @@ -55,10 +56,6 @@ class DocumentableList extends Component { } } -/** - * @typedef { { ref: Element; name: string; description: string } } ListElement - */ - class List { /** * @param tabsRef { Element[] } @@ -126,31 +123,49 @@ class List { * @param filter { Filter } */ isElementVisible(elementData, filter) { - if (!areFiltersFromElementSelected(elementData, filter)) { - return false; - } else { - return includesInputValue(elementData, filter); - } + return !areFiltersFromElementSelected() + ? false + : includesInputValue() function includesInputValue() { return elementData.name.includes(filter.value) || elementData.description.includes(filter.value); } function areFiltersFromElementSelected() { - /** @type {[key: string, value: string][]} */ - const dataset = Object.entries(elementData.ref.dataset).filter(([key]) => startsWith(key, "f")); - - const hasCorrespondingFilters = () => - dataset.every(([key, value]) => { - const filterGroup = filter.filters[key]; - return value.split(",").every(val => filterGroup && filterGroup[val].selected); - }); - - return dataset.length ? hasCorrespondingFilters() : true; + /** @param str { string } */ + const haveKeywordData = str => str.startsWith("f") + /** @type { [key: string, value: string][] } */ + const dataset = Object.entries(elementData.ref.dataset) + + const datasetWithDefaultData = dataset.filter(([key]) => !haveKeywordData(key)); + const datasetWithKeywordData = dataset.filter(([key]) => haveKeywordData(key)); + + return datasetWithKeywordData.length > 0 + ? hasCorrespondingFilters() + : haveDefaultFilters() + + function haveDefaultFilters() { + return ( + filter.filters.fKeywords[Filter.defaultFilterKey] && + filter.filters.fKeywords[Filter.defaultFilterKey].selected && + datasetWithDefaultData.length >= 1 + ) + } + + function hasCorrespondingFilters() { + return ( + datasetWithKeywordData + .every(([key, value]) => { + const filterGroup = filter.filters[key]; + return value.split(",").every(val => filterGroup && filterGroup[val].selected); + }) + ) + } } } /** + * @private * @param elementData { ListElement } */ _getTogglable = elementData => elementData.dataset.togglable; diff --git a/scala3doc/resources/dotty_res/scripts/components/Filter.js b/scala3doc/resources/dotty_res/scripts/components/Filter.js index ae256da33e66..da68f7642c41 100644 --- a/scala3doc/resources/dotty_res/scripts/components/Filter.js +++ b/scala3doc/resources/dotty_res/scripts/components/Filter.js @@ -17,6 +17,11 @@ class Filter { this._filters = this._init ? this._withNewFilters() : filters; } + /** + * Key for filters without the `fKeywords` + */ + static defaultFilterKey = 'default' + get value() { return this._value; } @@ -71,7 +76,7 @@ class Filter { * @returns { Filters } */ _generateFiltersOnTyping(value) { - return this.elementsRefs + const datasets = this.elementsRefs .filter((elRef) => { const name = getElementTextContent(getElementNameRef(elRef)); const description = getElementTextContent(getElementDescription(elRef)); @@ -79,16 +84,12 @@ class Filter { return name.includes(value) || description.includes(value); }) .map((elRef) => this._getDatasetWithKeywordData(elRef.dataset)) - .reduce((filtersObject, datasets) => { + + + return dataset.reduce((filtersObject, datasets) => { datasets.forEach(([key, value]) => { this._splitByComma(value).forEach((val) => { - filtersObject[key] = { - ...filtersObject[key], - [val]: { - ...filtersObject[key][val], - visible: true, - }, - }; + filtersObject[key] = { ...filtersObject[key], [val]: { ...filtersObject[key][val], visible: true} }; }); }); return filtersObject; @@ -157,7 +158,7 @@ class Filter { const shouldAddDefaultFilter = this._elementsRefs.some(ref => !!ref.dataset['fKeywords']) return shouldAddDefaultFilter - ? { ...newFilters, fKeywords: { ...newFilters.fKeywords, default: newFilter } } + ? { ...newFilters, fKeywords: { ...newFilters.fKeywords, [Filter.defaultFilterKey]: newFilter } } : newFilters } From 5ac73835ab4a1394bfa8da1606c37989cdea4793 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Tue, 3 Nov 2020 11:10:18 +0100 Subject: [PATCH 05/11] Attach default filters on input --- .../dotty_res/scripts/common/utils.js | 2 - .../scripts/components/DocumentableList.js | 3 +- .../dotty_res/scripts/components/Filter.js | 60 ++++++++++++++----- .../scripts/components/FilterGroup.js | 1 - 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/common/utils.js b/scala3doc/resources/dotty_res/scripts/common/utils.js index b61edc52d665..d69889e3d466 100644 --- a/scala3doc/resources/dotty_res/scripts/common/utils.js +++ b/scala3doc/resources/dotty_res/scripts/common/utils.js @@ -17,8 +17,6 @@ const attachDOM = (element, html) => { } }; -const startsWith = (str, character) => str.charAt(0) === character; - const htmlToString = (html) => { if (Array.isArray(html)) { return html.join(""); diff --git a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js index b4803aa06137..f812951ea949 100644 --- a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js +++ b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js @@ -133,7 +133,8 @@ class List { function areFiltersFromElementSelected() { /** @param str { string } */ - const haveKeywordData = str => str.startsWith("f") + const haveKeywordData = key => key === Filter.KeywordsKey + /** @type { [key: string, value: string][] } */ const dataset = Object.entries(elementData.ref.dataset) diff --git a/scala3doc/resources/dotty_res/scripts/components/Filter.js b/scala3doc/resources/dotty_res/scripts/components/Filter.js index da68f7642c41..cc0fe1b5bf2c 100644 --- a/scala3doc/resources/dotty_res/scripts/components/Filter.js +++ b/scala3doc/resources/dotty_res/scripts/components/Filter.js @@ -1,5 +1,4 @@ /** - * @typedef { { selected: boolean; visible: boolean } } FilterItem * @typedef { { fKeywords: Record } } Filters */ @@ -22,6 +21,11 @@ class Filter { */ static defaultFilterKey = 'default' + /** + * HTML data attribute that contains non-default keywords + */ + static KeywordsKey = 'fKeywords' + get value() { return this._value; } @@ -60,7 +64,6 @@ class Filter { /** * @param value { string } - * @returns { FilterItem } */ onInputValueChange(value) { return new Filter( @@ -76,24 +79,31 @@ class Filter { * @returns { Filters } */ _generateFiltersOnTyping(value) { - const datasets = this.elementsRefs - .filter((elRef) => { - const name = getElementTextContent(getElementNameRef(elRef)); - const description = getElementTextContent(getElementDescription(elRef)); + const elementsDatasets = this.elementsRefs + .filter(element => { + const name = getElementTextContent(getElementNameRef(element)); + const description = getElementTextContent(getElementDescription(element)); return name.includes(value) || description.includes(value); }) - .map((elRef) => this._getDatasetWithKeywordData(elRef.dataset)) - + .map(element => this._getDatasetWithKeywordData(element.dataset)) - return dataset.reduce((filtersObject, datasets) => { + const newFilters = elementsDatasets.reduce((filtersObject, datasets) => { datasets.forEach(([key, value]) => { this._splitByComma(value).forEach((val) => { filtersObject[key] = { ...filtersObject[key], [val]: { ...filtersObject[key][val], visible: true} }; }); }); + return filtersObject; }, this._allFiltersAreHidden()); + + const shouldAddDefaultFilter = elementsDatasets + .some(d => d.length === 0 || d.some(([key]) => key !== Filter.KeywordsKey)) + + return shouldAddDefaultFilter + ? this._attachDefaultFilters(newFilters) + : newFilters } /** @@ -142,26 +152,37 @@ class Filter { * @returns { Filters } */ _withNewFilters() { - const newFilter = { selected: true, visible: true } - const newFilters = this._elementsRefs.reduce((filtersObject, elementRef) => { this._getDatasetWithKeywordData(elementRef.dataset).forEach(([key, value]) => this._splitByComma(value).forEach((val) => { filtersObject[key] = filtersObject[key] - ? { ...filtersObject[key], [val]: filtersObject[key][val] ?? newFilter} - : { [val]: newFilter } + ? { ...filtersObject[key], [val]: filtersObject[key][val] ?? new FilterItem() } + : { [val]: new FilterItem() } }) ); return filtersObject; }, {}); - const shouldAddDefaultFilter = this._elementsRefs.some(ref => !!ref.dataset['fKeywords']) + const shouldAddDefaultFilter = this._elementsRefs.some(ref => !!ref.dataset[Filter.KeywordsKey]) return shouldAddDefaultFilter - ? { ...newFilters, fKeywords: { ...newFilters.fKeywords, [Filter.defaultFilterKey]: newFilter } } + ? this._attachDefaultFilters(newFilters) : newFilters } + /** + * @private + * @param {Filters} filters + */ + _attachDefaultFilters(filters) { + return { + ...filters, [Filter.KeywordsKey]: { + ...filters.fKeywords, + [Filter.defaultFilterKey]: new FilterItem() + } + } + } + /** * @private * @param key { string } @@ -193,5 +214,12 @@ class Filter { * @returns { [key: string, value: string][] } */ _getDatasetWithKeywordData = (dataset) => - Object.entries(dataset).filter(([key]) => startsWith(key, "f")); + Object.entries(dataset).filter(([key]) => key === Filter.KeywordsKey); } + +class FilterItem { + constructor(selected = true, visible = true) { + this.selected = selected + this.visible = visible + } +} \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js index 6065c582322d..ad28bfee1e00 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js @@ -99,7 +99,6 @@ class FilterGroup extends Component { } render({ filter }) { - console.log(filter.filters) attachDOM( this.filterLowerContainerRef, Object.entries(filter.filters) From 9fe6675808fb587e7845ee57f372586b1898b56b Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Tue, 3 Nov 2020 19:49:09 +0100 Subject: [PATCH 06/11] Add default filters for the rest of filter types --- .../dotty_res/scripts/common/utils.js | 4 ++ .../scripts/components/DocumentableList.js | 39 ++++++------- .../dotty_res/scripts/components/Filter.js | 55 +++++++++---------- 3 files changed, 50 insertions(+), 48 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/common/utils.js b/scala3doc/resources/dotty_res/scripts/common/utils.js index d69889e3d466..c600e1f85626 100644 --- a/scala3doc/resources/dotty_res/scripts/common/utils.js +++ b/scala3doc/resources/dotty_res/scripts/common/utils.js @@ -24,6 +24,10 @@ const htmlToString = (html) => { return html; }; +const isFilterData = key => key.startsWith("f") + +const getFilterKey = key => `f${key.charAt(0).toUpperCase()}${key.slice(1)}` + const attachListeners = (elementsRefs, type, callback) => elementsRefs.map((elRef) => withEvent(elRef, type, callback)); diff --git a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js index f812951ea949..31c9428f997c 100644 --- a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js +++ b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js @@ -132,35 +132,36 @@ class List { } function areFiltersFromElementSelected() { - /** @param str { string } */ - const haveKeywordData = key => key === Filter.KeywordsKey - /** @type { [key: string, value: string][] } */ const dataset = Object.entries(elementData.ref.dataset) - const datasetWithDefaultData = dataset.filter(([key]) => !haveKeywordData(key)); - const datasetWithKeywordData = dataset.filter(([key]) => haveKeywordData(key)); + const datasetWithFilterData = dataset.filter(([key]) => isFilterData(key)); - return datasetWithKeywordData.length > 0 - ? hasCorrespondingFilters() - : haveDefaultFilters() + return hasCorrespondingFilters() || haveDefaultFilters() function haveDefaultFilters() { return ( - filter.filters.fKeywords[Filter.defaultFilterKey] && - filter.filters.fKeywords[Filter.defaultFilterKey].selected && - datasetWithDefaultData.length >= 1 + Object.entries(Filter.defaultFilters).some(([key, value]) => { + const filterKey = getFilterKey(key) + + return ( + filter.filters[filterKey] && + filter.filters[filterKey][value].selected && + !dataset.some(([k]) => k === filterKey) + ) + } + ) ) } - + + // check if any selected filter is on data attr function hasCorrespondingFilters() { - return ( - datasetWithKeywordData - .every(([key, value]) => { - const filterGroup = filter.filters[key]; - return value.split(",").every(val => filterGroup && filterGroup[val].selected); - }) - ) + return datasetWithFilterData + .some(([filterKey, value]) => + value.split(",").some(val => + filter.filters[filterKey] && filter.filters[filterKey][val].selected + ) + ) } } } diff --git a/scala3doc/resources/dotty_res/scripts/components/Filter.js b/scala3doc/resources/dotty_res/scripts/components/Filter.js index cc0fe1b5bf2c..87181c91b0fe 100644 --- a/scala3doc/resources/dotty_res/scripts/components/Filter.js +++ b/scala3doc/resources/dotty_res/scripts/components/Filter.js @@ -1,5 +1,7 @@ /** - * @typedef { { fKeywords: Record } } Filters + * @typedef { Record } FilterMap + * @typedef { "fKeywords" | "fInherited" | "fImplicitly" | "fExtension" | "fVisibility" } FilterAttributes + * @typedef { Record } Filters */ class Filter { @@ -16,15 +18,9 @@ class Filter { this._filters = this._init ? this._withNewFilters() : filters; } - /** - * Key for filters without the `fKeywords` - */ - static defaultFilterKey = 'default' - - /** - * HTML data attribute that contains non-default keywords - */ - static KeywordsKey = 'fKeywords' + static get defaultFilters() { + return scala3DocData.filterDefaults + } get value() { return this._value; @@ -98,12 +94,8 @@ class Filter { return filtersObject; }, this._allFiltersAreHidden()); - const shouldAddDefaultFilter = elementsDatasets - .some(d => d.length === 0 || d.some(([key]) => key !== Filter.KeywordsKey)) + return this._attachDefaultFilters(newFilters) - return shouldAddDefaultFilter - ? this._attachDefaultFilters(newFilters) - : newFilters } /** @@ -163,24 +155,29 @@ class Filter { return filtersObject; }, {}); - const shouldAddDefaultFilter = this._elementsRefs.some(ref => !!ref.dataset[Filter.KeywordsKey]) - - return shouldAddDefaultFilter - ? this._attachDefaultFilters(newFilters) - : newFilters + return this._attachDefaultFilters(newFilters) } /** * @private - * @param {Filters} filters + * @param {Filters} newFilters + * @returns {Filters} */ - _attachDefaultFilters(filters) { - return { - ...filters, [Filter.KeywordsKey]: { - ...filters.fKeywords, - [Filter.defaultFilterKey]: new FilterItem() - } - } + _attachDefaultFilters(newFilters) { + return Object.entries(Filter.defaultFilters).reduce((acc, [key, defaultFilter]) => { + const filterKey = getFilterKey(key) + const shouldAddDefaultKeywordFilter = this._elementsRefs.some(ref => !!ref.dataset[filterKey]) + + return shouldAddDefaultKeywordFilter + ? { + ...acc, + [filterKey]: { + ...acc[filterKey], + [defaultFilter]: new FilterItem() + } + } + : acc + }, newFilters) } /** @@ -214,7 +211,7 @@ class Filter { * @returns { [key: string, value: string][] } */ _getDatasetWithKeywordData = (dataset) => - Object.entries(dataset).filter(([key]) => key === Filter.KeywordsKey); + Object.entries(dataset).filter(([key]) => isFilterData(key)); } class FilterItem { From f7d61c563c742f17475c68eca7c696180732da4f Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Tue, 3 Nov 2020 22:11:48 +0100 Subject: [PATCH 07/11] Use && for filter groups and || for group members --- .../scripts/components/DocumentableList.js | 68 ++++++++++--------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js index 31c9428f997c..41c31a1e539d 100644 --- a/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js +++ b/scala3doc/resources/dotty_res/scripts/components/DocumentableList.js @@ -1,6 +1,7 @@ /** * @typedef { import("./Filter").Filter } Filter * @typedef { { ref: Element; name: string; description: string } } ListElement + * @typedef { [key: string, value: string][] } Dataset */ class DocumentableList extends Component { @@ -132,37 +133,42 @@ class List { } function areFiltersFromElementSelected() { - /** @type { [key: string, value: string][] } */ - const dataset = Object.entries(elementData.ref.dataset) - - const datasetWithFilterData = dataset.filter(([key]) => isFilterData(key)); - - return hasCorrespondingFilters() || haveDefaultFilters() - - function haveDefaultFilters() { - return ( - Object.entries(Filter.defaultFilters).some(([key, value]) => { - const filterKey = getFilterKey(key) - - return ( - filter.filters[filterKey] && - filter.filters[filterKey][value].selected && - !dataset.some(([k]) => k === filterKey) - ) - } - ) - ) - } - - // check if any selected filter is on data attr - function hasCorrespondingFilters() { - return datasetWithFilterData - .some(([filterKey, value]) => - value.split(",").some(val => - filter.filters[filterKey] && filter.filters[filterKey][val].selected - ) - ) - } + /** @type { Dataset } */ + const dataset = Object.entries(elementData.ref.dataset) + + /** @type { Dataset } */ + const defaultFilters = Object.entries(Filter.defaultFilters) + .filter(([key]) => !!filter.filters[getFilterKey(key)]) + + /** @type { Dataset } */ + const defaultFiltersForMembersWithoutDataAttribute = + defaultFilters.reduce((acc, [key, value]) => { + const filterKey = getFilterKey(key) + const shouldAddDefaultFilter = !dataset.some(([k]) => k === filterKey) + return shouldAddDefaultFilter ? [...acc, [filterKey, value]] : acc + }, []) + + /** @type { Dataset } */ + const datasetWithAppendedDefaultFilters = dataset + .filter(([k]) => isFilterData(k)) + .map(([k, v]) => { + const defaultFilter = defaultFilters.find(([defaultKey]) => defaultKey === k) + return defaultFilter ? [k, `${v},${defaultFilter[1]}`] : [k, v] + }) + + const datasetWithDefaultFilters = [ + ...defaultFiltersForMembersWithoutDataAttribute, + ...datasetWithAppendedDefaultFilters + ] + + const isVisible = datasetWithDefaultFilters + .every(([filterKey, value]) => { + const filterGroup = filter.filters[filterKey] + + return value.split(",").some(v => filterGroup && filterGroup[v].selected) + }) + + return isVisible } } From 1c7da87067f118e8aa96fb415d6cf3e29114d180 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Wed, 4 Nov 2020 10:17:59 +0100 Subject: [PATCH 08/11] Always show default filters first --- .../scripts/components/FilterGroup.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js index ad28bfee1e00..8e7affefd2c3 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js @@ -68,29 +68,34 @@ class FilterGroup extends Component { return visible ? "visible" : ""; } - getSortedValues(values) { - return Object.entries(values).sort((a, b) => a[0].localeCompare(b[0])); + getSortedValues(filterKey, values) { + const defaultFilterKey = `${filterKey.charAt(1).toLowerCase()}${filterKey.slice(2)}` + const defaultGroupFilter = Filter.defaultFilters[defaultFilterKey] + + return Object.entries(values).sort(([a], [b]) => + a === defaultGroupFilter || b === defaultGroupFilter ? 1 : a.localeCompare(b) + ) } - getFilterGroup(title, values) { + getFilterGroup(filterKey, values) { return `
- ${title.substring(1)} + ${filterKey.substring(1)}
- - + +
- ${this.getSortedValues(values) + ${this.getSortedValues(filterKey, values) .map( ([key, data]) => `` + )}" data-key="${filterKey}" data-value="${key}">${key}` ) .join(" ")}
@@ -102,7 +107,7 @@ class FilterGroup extends Component { attachDOM( this.filterLowerContainerRef, Object.entries(filter.filters) - .filter(([key, values]) => Object.values(values).some((v) => v.visible)) + .filter(([_key, values]) => Object.values(values).some((v) => v.visible)) .map(([key, values]) => this.getFilterGroup(key, values)) ); From 1d3e4f1ff78d8a754d303de5c7cde780ec182e9e Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Thu, 5 Nov 2020 10:13:00 +0100 Subject: [PATCH 09/11] Use project scoped cmds in the README --- scala3doc/README.md | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/scala3doc/README.md b/scala3doc/README.md index 5f065c3f70cb..b2ee48d8bb8b 100644 --- a/scala3doc/README.md +++ b/scala3doc/README.md @@ -17,18 +17,11 @@ We're aiming to support all the features Scaladoc did, plus new and exciting one ## Running the project -Run `sbt` and switch to scala3doc project from the root +Use the following commands to generate documentation for this project and for Dotty, respectively: ``` -sbt -project scala3doc -``` - -When in the `scala3doc` project in `sbt`, use the following commands to generate documentation for this project and for Dotty, respectively: - -``` -generateSelfDocumentation -generateScala3Documentation +sbt scala3doc/generateSelfDocumentation +sbt scala3doc/generateScala3Documentation ``` To actually view the documentation, the easiest way is to run the following in project root: @@ -48,15 +41,16 @@ the documentation won't work completely if you don't. ## CLI Documentation CLI command for running our tool is in form: `sbt main -n -o -t -cp -s { } -d ` where: - - ``: name of module in generated documentation - - ``: location where documentation should be created - - ``: is list of dirs or jars that contains tasty files that should be documented - - ``: classpath that was used to generate tasty files - - ``: links to source files of module that are used to link symbols on pages to their source file. They need to be supplied in form: - `local_dir=remote_dir#line_suffix` e.g. `src/main/scala=https://github.com/lampepfl/scala3doc/tree/master/src/main/scala#L` - - ``: directory of static documentation that you would like to render with API documentation. This feature is provided by dokka-site plugin: - - [GitHub](https://github.com/VirtusLab/dokka-site) - - [Documentation](https://virtuslab.github.io/dokka-site/index.html) + +- ``: name of module in generated documentation +- ``: location where documentation should be created +- ``: is list of dirs or jars that contains tasty files that should be documented +- ``: classpath that was used to generate tasty files +- ``: links to source files of module that are used to link symbols on pages to their source file. They need to be supplied in form: + `local_dir=remote_dir#line_suffix` e.g. `src/main/scala=https://github.com/lampepfl/scala3doc/tree/master/src/main/scala#L` +- ``: directory of static documentation that you would like to render with API documentation. This feature is provided by dokka-site plugin: + - [GitHub](https://github.com/VirtusLab/dokka-site) + - [Documentation](https://virtuslab.github.io/dokka-site/index.html) ## Developing @@ -66,16 +60,16 @@ work on the project. For every PR, we build documentation for Scala3doc and Dotty. For example, for PR 123 you can find them at: -+ -+ +- +- Note that these correspond to the contents of `output` directory - that's precisely what they are. You can also find the result of building the same sites for latest `master` at: -+ -+ +- +- ### Testing From ea1c0c6445367ae691fb8542ead1012bae9ffddd Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Fri, 6 Nov 2020 11:38:11 +0100 Subject: [PATCH 10/11] Set up e2e tests for scala3doc --- scala3doc/e2e/.prettierrc | 9 + scala3doc/e2e/README.md | 12 + scala3doc/e2e/cypress.json | 1 + .../cypress/integration/filter-bar.spec.ts | 60 + scala3doc/e2e/cypress/support/commands.ts | 1 + scala3doc/e2e/cypress/support/index.ts | 4 + scala3doc/e2e/package-lock.json | 2057 +++++++++++++++++ scala3doc/e2e/package.json | 18 + scala3doc/e2e/tsconfig.json | 10 + .../scripts/components/FilterGroup.js | 22 +- .../dotty/renderers/ScalaHtmlRenderer.scala | 2 +- scala3doc/src/dotty/renderers/html.scala | 1 + 12 files changed, 2189 insertions(+), 8 deletions(-) create mode 100644 scala3doc/e2e/.prettierrc create mode 100644 scala3doc/e2e/README.md create mode 100644 scala3doc/e2e/cypress.json create mode 100644 scala3doc/e2e/cypress/integration/filter-bar.spec.ts create mode 100644 scala3doc/e2e/cypress/support/commands.ts create mode 100644 scala3doc/e2e/cypress/support/index.ts create mode 100644 scala3doc/e2e/package-lock.json create mode 100644 scala3doc/e2e/package.json create mode 100644 scala3doc/e2e/tsconfig.json diff --git a/scala3doc/e2e/.prettierrc b/scala3doc/e2e/.prettierrc new file mode 100644 index 000000000000..2fd31d7ed46b --- /dev/null +++ b/scala3doc/e2e/.prettierrc @@ -0,0 +1,9 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "avoid" +} diff --git a/scala3doc/e2e/README.md b/scala3doc/e2e/README.md new file mode 100644 index 000000000000..61b4b59fb2af --- /dev/null +++ b/scala3doc/e2e/README.md @@ -0,0 +1,12 @@ +## scala3doc e2e test suite + +### Prerequisites + +- install Node.js +- run `npm i` + +### Running tests + +- generate the test docs: `sbt scala3doc/generateTestcasesDocumentation` +- run the web server in the `output` directory: `python3 -m http.server 8080` +- run `npm run cypress:open` to see the cypress UI or `npm run cypress:run` to run tests heedlessly diff --git a/scala3doc/e2e/cypress.json b/scala3doc/e2e/cypress.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/scala3doc/e2e/cypress.json @@ -0,0 +1 @@ +{} diff --git a/scala3doc/e2e/cypress/integration/filter-bar.spec.ts b/scala3doc/e2e/cypress/integration/filter-bar.spec.ts new file mode 100644 index 000000000000..aa58acd68ac4 --- /dev/null +++ b/scala3doc/e2e/cypress/integration/filter-bar.spec.ts @@ -0,0 +1,60 @@ +describe('filter-bar', () => { + beforeEach(() => cy.visit('http://localhost:8080/testcases/api/tests/-filter-test/index.html')); + + it('properly renders filters extracted from the document', () => { + const filterBar = new FilterBarFixture().toggle(); + + const testTable = [ + ['Visibility', ['public', 'protected']], + ['Keywords', ['no keywords', 'abstract', 'case', 'final', 'sealed']], + ['Extension', ['Standard member', 'from tests']], + ]; + + testTable.forEach(([title, filterOptions], index) => { + const group = filterBar.group(index); + + group.title.should('have.text', title); + group.filterOptions.should('deep.equal', filterOptions); + }); + }); + + it('filters by && across groups', () => {}); +}); + +class FilterBarFixture { + private get toggleButton() { + return cy.findByTestId('filterToggleButton'); + } + + group(at: number) { + return new FilterBarGroupFixture(at); + } + + toggle() { + this.toggleButton.click(); + + return this; + } +} + +class FilterBarGroupFixture { + constructor(private readonly index: number) {} + + private get group() { + return cy.findAllByTestId('filterGroup').eq(this.index); + } + + private get filterList() { + return this.group.findByTestId('filterGroupList'); + } + + get title() { + return this.group.findByTestId('filterGroupTitle'); + } + + get filterOptions() { + return this.filterList + .findAllByTestId('filterGroupButton') + .then($buttons => cy.wrap($buttons.toArray().map(i => i.innerText))); + } +} diff --git a/scala3doc/e2e/cypress/support/commands.ts b/scala3doc/e2e/cypress/support/commands.ts new file mode 100644 index 000000000000..44cbd485bc2e --- /dev/null +++ b/scala3doc/e2e/cypress/support/commands.ts @@ -0,0 +1 @@ +import "@testing-library/cypress/add-commands"; diff --git a/scala3doc/e2e/cypress/support/index.ts b/scala3doc/e2e/cypress/support/index.ts new file mode 100644 index 000000000000..8dc2f81f81f8 --- /dev/null +++ b/scala3doc/e2e/cypress/support/index.ts @@ -0,0 +1,4 @@ +import './commands'; +import { configure } from '@testing-library/cypress'; + +configure({ testIdAttribute: 'data-test-id' }); diff --git a/scala3doc/e2e/package-lock.json b/scala3doc/e2e/package-lock.json new file mode 100644 index 000000000000..3f09b6e3afff --- /dev/null +++ b/scala3doc/e2e/package-lock.json @@ -0,0 +1,2057 @@ +{ + "name": "scala3doc-e2e", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@cypress/listr-verbose-renderer": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", + "integrity": "sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-cursor": "^1.0.2", + "date-fns": "^1.27.2", + "figures": "^1.7.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "@cypress/request": { + "version": "2.88.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.5.tgz", + "integrity": "sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@samverschueren/stream-to-observable": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", + "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, + "@testing-library/cypress": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/cypress/-/cypress-7.0.1.tgz", + "integrity": "sha512-LtggqG/7Hdc1EiKdmqXQwxWOO3ET1dkZtq0S8mIe8o+xaOtaVLrdCn0dE8Bi4Aj7z3w51w6wN9STdYymnUPlnQ==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "@testing-library/dom": "^7.22.2" + } + }, + "@testing-library/dom": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.26.5.tgz", + "integrity": "sha512-2v/fv0s4keQjJIcD4bjfJMFtvxz5icartxUWdIZVNJR539WD9oxVrvIAPw+3Ydg4RLgxt0rvQx3L9cAjCci0Kg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.10.3", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.1", + "lz-string": "^1.4.4", + "pretty-format": "^26.4.2" + } + }, + "@types/aria-query": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/node": { + "version": "14.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz", + "integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==", + "dev": true + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", + "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "dev": true + }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + } + } + }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", + "integrity": "sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + } + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "optional": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "common-tags": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cypress": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-5.5.0.tgz", + "integrity": "sha512-UHEiTca8AUTevbT2pWkHQlxoHtXmbq+h6Eiu/Mz8DqpNkF98zjTBLv/HFiKJUU5rQzp9EwSWtms33p5TWCJ8tQ==", + "dev": true, + "requires": { + "@cypress/listr-verbose-renderer": "^0.4.1", + "@cypress/request": "^2.88.5", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "^6.0.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.1.2", + "blob-util": "2.0.2", + "bluebird": "^3.7.2", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-table3": "~0.6.0", + "commander": "^4.1.1", + "common-tags": "^1.8.0", + "debug": "^4.1.1", + "eventemitter2": "^6.4.2", + "execa": "^4.0.2", + "executable": "^4.1.1", + "extract-zip": "^1.7.0", + "fs-extra": "^9.0.1", + "getos": "^3.2.1", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.2", + "lazy-ass": "^1.6.0", + "listr": "^0.14.3", + "lodash": "^4.17.19", + "log-symbols": "^4.0.0", + "minimist": "^1.2.5", + "moment": "^2.27.0", + "ospath": "^1.2.2", + "pretty-bytes": "^5.4.1", + "ramda": "~0.26.1", + "request-progress": "^3.0.0", + "supports-color": "^7.2.0", + "tmp": "~0.2.1", + "untildify": "^4.0.0", + "url": "^0.11.0", + "yauzl": "^2.10.0" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "dom-accessibility-api": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eventemitter2": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", + "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + } + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dev": true, + "requires": { + "concat-stream": "^1.6.2", + "debug": "^2.6.9", + "mkdirp": "^0.5.4", + "yauzl": "^2.10.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "requires": { + "async": "^3.2.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "dev": true, + "requires": { + "ini": "^1.3.5" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + } + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", + "dev": true + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha1-eZllXoZGwX8In90YfRUNMyTVRRM=", + "dev": true + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "dependencies": { + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + } + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs=", + "dev": true + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pretty-bytes": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "ramda": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz", + "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==", + "dev": true + }, + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "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" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", + "dev": true + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/scala3doc/e2e/package.json b/scala3doc/e2e/package.json new file mode 100644 index 000000000000..7f47fc7c86dc --- /dev/null +++ b/scala3doc/e2e/package.json @@ -0,0 +1,18 @@ +{ + "name": "scala3doc-e2e", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "cypress:open": "cypress open", + "cypress:run": "cypress run" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@testing-library/cypress": "^7.0.1", + "@types/node": "^14.14.6", + "cypress": "^5.5.0", + "typescript": "^4.0.5" + } +} diff --git a/scala3doc/e2e/tsconfig.json b/scala3doc/e2e/tsconfig.json new file mode 100644 index 000000000000..cd4b429240d0 --- /dev/null +++ b/scala3doc/e2e/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["es5", "dom", ], + "types": ["cypress", "@testing-library/cypress", "node"] + }, + "include": [ + "**/*.ts" + ] + } \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js index 8e7affefd2c3..751f60d897b4 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js @@ -72,22 +72,30 @@ class FilterGroup extends Component { const defaultFilterKey = `${filterKey.charAt(1).toLowerCase()}${filterKey.slice(2)}` const defaultGroupFilter = Filter.defaultFilters[defaultFilterKey] - return Object.entries(values).sort(([a], [b]) => - a === defaultGroupFilter || b === defaultGroupFilter ? 1 : a.localeCompare(b) - ) + return Object.entries(values).sort(([a], [b]) => { + if (a === defaultGroupFilter) { + return -1 + } + + if (b === defaultGroupFilter) { + return 1 + } + + return a.localeCompare(b) + }) } getFilterGroup(filterKey, values) { return ` -
+
- ${filterKey.substring(1)} + ${filterKey.substring(1)}
-
+
${this.getSortedValues(filterKey, values) .map( ([key, data]) => @@ -95,7 +103,7 @@ class FilterGroup extends Component { data.selected )} ${this.isVisible( data.visible - )}" data-key="${filterKey}" data-value="${key}">${key}` + )}" data-key="${filterKey}" data-value="${key}" data-test-id="filterGroupButton">${key}` ) .join(" ")}
diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 32dbd744f530..bdc4d829f59c 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -146,7 +146,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { private def buildDocumentableFilter = div(cls := "documentableFilter")( div(cls := "filterUpperContainer")( - button(cls := "filterToggleButton")( + button(cls := "filterToggleButton", testId := "filterToggleButton")( raw(""" diff --git a/scala3doc/src/dotty/renderers/html.scala b/scala3doc/src/dotty/renderers/html.scala index 6b72f50fc76b..f19b0ac288d9 100644 --- a/scala3doc/src/dotty/renderers/html.scala +++ b/scala3doc/src/dotty/renderers/html.scala @@ -89,6 +89,7 @@ object HTML: val charset = Attr("charset") val name = Attr("name") val content = Attr("content") + val testId = Attr("data-test-id") def raw(content: String): AppliedTag = AppliedTag(content) From 5f7d2b8f624937c4c8bde646a2eee6dbdc65a364 Mon Sep 17 00:00:00 2001 From: Grzegorz Bielski Date: Fri, 6 Nov 2020 14:52:25 +0100 Subject: [PATCH 11/11] Add tests for filter bar widget --- .../cypress/integration/filter-bar.spec.ts | 269 +++++++++++++++++- .../scripts/components/FilterGroup.js | 4 +- .../dotty/renderers/ScalaHtmlRenderer.scala | 4 +- 3 files changed, 260 insertions(+), 17 deletions(-) diff --git a/scala3doc/e2e/cypress/integration/filter-bar.spec.ts b/scala3doc/e2e/cypress/integration/filter-bar.spec.ts index aa58acd68ac4..fb2a1fbefe66 100644 --- a/scala3doc/e2e/cypress/integration/filter-bar.spec.ts +++ b/scala3doc/e2e/cypress/integration/filter-bar.spec.ts @@ -1,24 +1,170 @@ describe('filter-bar', () => { - beforeEach(() => cy.visit('http://localhost:8080/testcases/api/tests/-filter-test/index.html')); + beforeEach(openPage); it('properly renders filters extracted from the document', () => { const filterBar = new FilterBarFixture().toggle(); - const testTable = [ + const testTable: TestTable = [ ['Visibility', ['public', 'protected']], ['Keywords', ['no keywords', 'abstract', 'case', 'final', 'sealed']], ['Extension', ['Standard member', 'from tests']], ]; - testTable.forEach(([title, filterOptions], index) => { - const group = filterBar.group(index); + testFilterBarOptions(filterBar, testTable); + }); - group.title.should('have.text', title); - group.filterOptions.should('deep.equal', filterOptions); - }); + it('properly filters the definition list through search box', () => { + const tabs = new TabsFixture(); + const filterBar = new FilterBarFixture().toggle(); + + // type + tabs.definition('publicType').should('be.visible'); + // protected type + tabs.definition('protectedType').should('be.visible'); + + filterBar.input.type('protectedType'); + + // protected type + tabs.definition('protectedType').should('be.visible'); + // type + tabs.definition('publicType').should('not.be.visible'); + + const testTable: TestTable = [ + ['Visibility', ['public', 'protected']], + ['Keywords', ['no keywords']], + ['Extension', ['Standard member']], + ]; + + testFilterBarOptions(filterBar, testTable); + }); + + it('works with select all / deselect all', () => { + const filterBar = new FilterBarFixture().toggle(); + const group = filterBar.group(0); + const batchSelection = filterBar.group(0).batchSelection; + + const public = () => group.filterOption('public').then(x => x.isSelected); + const protected = () => group.filterOption('protected').then(x => x.isSelected); + + public().should('be.equal', true); + protected().should('be.equal', true); + + batchSelection.deselectAll(); + + public().should('be.equal', false); + protected().should('be.equal', false); + + batchSelection.selectAll(); + + public().should('be.equal', true); + protected().should('be.equal', true); }); - it('filters by && across groups', () => {}); + describe('filter configurations', () => { + describe('returns empty list after deselecting', () => { + it(`'public' and 'no keywords'`, () => { + const filterBar = new FilterBarFixture().toggle(); + filterBar.group(0).toggleFilter('public'); + filterBar.group(1).toggleFilter('no keywords'); + + new TabsFixture().definitionTypes.should('not.be.visible'); + }); + + it(`'Standard member'`, () => { + new FilterBarFixture().toggle().group(2).toggleFilter('Standard member'); + + new TabsFixture().definitionTypes.should('not.be.visible'); + }); + + it('all visibility options', () => { + new FilterBarFixture().toggle().group(0).toggleFilter('public', 'protected'); + + new TabsFixture().definitionTypes.should('not.be.visible'); + }); + + it('all keywords options', () => { + new FilterBarFixture() + .toggle() + .group(1) + .toggleFilter('no keywords', 'abstract', 'case', 'final', 'sealed'); + + new TabsFixture().definitionTypes.should('not.be.visible'); + }); + + it('all extension options', () => { + new FilterBarFixture().toggle().group(2).toggleFilter('Standard member', 'from tests'); + + new TabsFixture().definitionTypes.should('not.be.visible'); + }); + }); + + describe('returns filtered list after deselecting', () => { + it(`'protected'`, () => { + const tabs = new TabsFixture(); + + tabs.definition('protected').should('be.visible'); + new FilterBarFixture().toggle().group(0).toggleFilter('protected'); + tabs.definition('protected').should('not.be.visible'); + }); + + it(`'no keywords', 'case', 'final' and 'sealed'`, () => { + const tabs = new TabsFixture(); + + // protected object + tabs.definition('ProtectedObject').should('be.visible'); + + // sealed case class + tabs.definition('D').should('be.visible'); + + // final case class + tabs.definition('E').should('be.visible'); + + new FilterBarFixture() + .toggle() + .group(1) + .toggleFilter('no keywords', 'case', 'final', 'sealed'); + + // protected object + tabs.definition('ProtectedObject').should('not.be.visible'); + + // sealed abstract class + tabs.definition('B').should('be.visible'); + + // abstract case class + tabs.definition('C').should('be.visible'); + + // sealed case class + tabs.definition('D').should('not.be.visible'); + + // final case class + tabs.definition('E').should('not.be.visible'); + }); + + it(`'no keywords', 'final' and 'sealed'`, () => { + const tabs = new TabsFixture(); + + // protected object + tabs.definition('ProtectedObject').should('be.visible'); + + new FilterBarFixture().toggle().group(1).toggleFilter('no keywords', 'final', 'sealed'); + + // protected object + tabs.definition('ProtectedObject').should('not.be.visible'); + + // sealed abstract class + tabs.definition('B').should('be.visible'); + + // abstract case class + tabs.definition('C').should('be.visible'); + + // sealed case class + tabs.definition('D').should('be.visible'); + + // final case class + tabs.definition('E').should('be.visible'); + }); + }); + }); }); class FilterBarFixture { @@ -30,6 +176,10 @@ class FilterBarFixture { return new FilterBarGroupFixture(at); } + get input() { + return new FilterInputFixture(); + } + toggle() { this.toggleButton.click(); @@ -44,17 +194,110 @@ class FilterBarGroupFixture { return cy.findAllByTestId('filterGroup').eq(this.index); } - private get filterList() { - return this.group.findByTestId('filterGroupList'); + private get filterButtons() { + return this.group + .findByTestId('filterGroupList') + .findAllByTestId('filterGroupButton') + .filter(':visible'); } get title() { return this.group.findByTestId('filterGroupTitle'); } + get batchSelection() { + return new BatchSelectionFixture(() => this.group); + } + + get filterOptionsValues() { + return this.filterOptions.then(options => { + const acc: string[] = []; + options.forEach(o => o.name.then(v => acc.push(v))); + return cy.wrap(acc); + }); + } + + filterOption(name: string) { + return this.filterButtons + .contains(name) + .then($el => new FilterOptionFixture(() => cy.wrap($el))); + } + get filterOptions() { - return this.filterList - .findAllByTestId('filterGroupButton') - .then($buttons => cy.wrap($buttons.toArray().map(i => i.innerText))); + return ( + this.filterButtons + // .filter(':visible') + .then($buttons => + cy.wrap($buttons.toArray().map(el => new FilterOptionFixture(() => cy.wrap(el)))), + ) + ); + } + + toggleFilter(...names: string[]) { + names.forEach(name => this.filterButtons.contains(name).click()); + return this; + } +} + +class FilterOptionFixture { + constructor(private readonly root: () => Cypress.Chainable>) {} + + get name() { + return this.root().then($el => $el.text()); + } + + get isSelected() { + return this.root().then($el => cy.wrap($el.data('selected'))); + } +} + +class TabsFixture { + get definitionTypes() { + return cy.findAllByTestId('definitionList'); + } + + definition(name: string) { + return this.definitionTypes.contains(name); + } +} + +class FilterInputFixture { + private get input() { + return cy.findByTestId('filterBarInput'); + } + + type(phrase: string) { + this.input.type(phrase); + } +} + +class BatchSelectionFixture { + constructor(private readonly root: () => Cypress.Chainable>) {} + + private get container() { + return this.root().findByTestId('filterGroupBatchToggle'); + } + + selectAll() { + this.container.findByText('Select All').click(); } + + deselectAll() { + this.container.findByText('Deselect All').click(); + } +} + +function openPage() { + cy.visit('http://localhost:8080/testcases/api/tests/-filter-test/index.html'); +} + +type TestTable = [string, string[]][]; + +function testFilterBarOptions(filterBar: FilterBarFixture, testTable: TestTable) { + testTable.forEach(([title, filterOptions], index) => { + const group = filterBar.group(index); + + group.title.should('have.text', title); + group.filterOptionsValues.should('deep.equal', filterOptions); + }); } diff --git a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js index 751f60d897b4..96606b2c8d68 100644 --- a/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js +++ b/scala3doc/resources/dotty_res/scripts/components/FilterGroup.js @@ -90,7 +90,7 @@ class FilterGroup extends Component {
${filterKey.substring(1)} -
+
@@ -103,7 +103,7 @@ class FilterGroup extends Component { data.selected )} ${this.isVisible( data.visible - )}" data-key="${filterKey}" data-value="${key}" data-test-id="filterGroupButton">${key}` + )}" data-key="${filterKey}" data-selected="${data.selected}" data-value="${key}" data-test-id="filterGroupButton">${key}` ) .join(" ")}
diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index bdc4d829f59c..bd873f6922d2 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -132,7 +132,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { ) - div(cls := "documentableList")( + div(cls := "documentableList", testId := "definitionList")( if(n.groupName.isEmpty) raw("") else h3(cls := "documentableHeader")(n.groupName.map(renderElement)), n.elements.flatMap { case element: DocumentableElement => @@ -154,7 +154,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends HtmlRenderer(ctx) { """) ), - input(cls := "filterableInput", placeholder := "Filter all members") + input(cls := "filterableInput", placeholder := "Filter all members", testId := "filterBarInput") ), div(cls := "filterLowerContainer")() )