From 0fa9314d3b8c8b2e3e25248956a47beb2c5df26d Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:52:37 -0400 Subject: [PATCH 01/19] DirectAnswers on Vertical: Templates + Analytics (#994) - move directanswers markup and script to common-partials folder - update vertical standard templates to include direct answer - update direct answer base card component to include the correct searcher (Universal or Vertical) - add and comment out directanswer config for vertical standard page NOTE: this requires [changes in SDK](https://github.com/yext/answers-search-ui/pull/1596) to work J=SLAP-1674 T=manual change people vertical page to vertical-standard with directanswer configs/templates uncommented. serve test-site, and perform 'bryan phone number' on universal page and people page. See that direct answers appear for both page. When click thumbs up, see that the appropriate analytics payload is send in the request. --- directanswercards/card_component.js | 8 ++++---- templates/common-partials/markup/directanswer.hbs | 1 + templates/common-partials/script/directanswer.hbs | 9 +++++++++ templates/universal-standard/markup/directanswer.hbs | 2 +- templates/universal-standard/script/directanswer.hbs | 10 +--------- templates/vertical-standard/markup/directanswer.hbs | 1 + templates/vertical-standard/page-config.json | 12 ++++++++++++ templates/vertical-standard/page.html.hbs | 3 +++ templates/vertical-standard/script/directanswer.hbs | 1 + tests/templates/script/fixtures/directanswer.hbs.js | 2 ++ 10 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 templates/common-partials/markup/directanswer.hbs create mode 100644 templates/common-partials/script/directanswer.hbs create mode 100644 templates/vertical-standard/markup/directanswer.hbs create mode 100644 templates/vertical-standard/script/directanswer.hbs create mode 100644 tests/templates/script/fixtures/directanswer.hbs.js diff --git a/directanswercards/card_component.js b/directanswercards/card_component.js index 5593bf584..0fadc0462 100644 --- a/directanswercards/card_component.js +++ b/directanswercards/card_component.js @@ -23,9 +23,9 @@ BaseDirectAnswerCard["{{componentName}}"] = class extends ANSWERS.Component { setState(data) { let cardData = this.dataForRender(this.type, this.answer, this.relatedItem, this.snippet); this.validateDataForRender(cardData); - return super.setState({ ...cardData, + searcher: data.searcher, feedbackEnabled: ANSWERS.getAnalyticsOptIn(), feedbackSubmitted: data.feedbackSubmitted, isArray: Array.isArray(this.answer.value), @@ -97,7 +97,7 @@ BaseDirectAnswerCard["{{componentName}}"] = class extends ANSWERS.Component { .addOptions({ directAnswer: true, verticalKey: this.verticalConfigId, - searcher: 'UNIVERSAL', + searcher: this.getState('searcher'), entityId: this.associatedEntityId }); @@ -120,7 +120,7 @@ BaseDirectAnswerCard["{{componentName}}"] = class extends ANSWERS.Component { */ addDefaultEventOptions(eventOptions = {}) { return Object.assign({}, { - searcher: "UNIVERSAL", + searcher: this.getState('searcher'), verticalConfigId: this.verticalConfigId, entityId: this.associatedEntityId, ...eventOptions @@ -144,7 +144,7 @@ BaseDirectAnswerCard["{{componentName}}"] = class extends ANSWERS.Component { verticalKey: this.verticalConfigId, directAnswer: true, fieldName: this.answer.fieldApiName, - searcher: 'UNIVERSAL', + searcher: this.getState('searcher'), entityId: this.associatedEntityId, url: event.target.href }; diff --git a/templates/common-partials/markup/directanswer.hbs b/templates/common-partials/markup/directanswer.hbs new file mode 100644 index 000000000..5f000c6a7 --- /dev/null +++ b/templates/common-partials/markup/directanswer.hbs @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/templates/common-partials/script/directanswer.hbs b/templates/common-partials/script/directanswer.hbs new file mode 100644 index 000000000..7d383bd9a --- /dev/null +++ b/templates/common-partials/script/directanswer.hbs @@ -0,0 +1,9 @@ +ANSWERS.addComponent("DirectAnswer", +{{#shallowMergeConfig}}[ + { + "container": "#js-answersDirectAnswer" + }, + {{{json componentSettings.DirectAnswer}}} +] +{{/shallowMergeConfig}} +); diff --git a/templates/universal-standard/markup/directanswer.hbs b/templates/universal-standard/markup/directanswer.hbs index 5f000c6a7..e22487a72 100644 --- a/templates/universal-standard/markup/directanswer.hbs +++ b/templates/universal-standard/markup/directanswer.hbs @@ -1 +1 @@ -
\ No newline at end of file +{{> templates/common-partials/markup/directanswer }} \ No newline at end of file diff --git a/templates/universal-standard/script/directanswer.hbs b/templates/universal-standard/script/directanswer.hbs index 7d383bd9a..b9007a58e 100644 --- a/templates/universal-standard/script/directanswer.hbs +++ b/templates/universal-standard/script/directanswer.hbs @@ -1,9 +1 @@ -ANSWERS.addComponent("DirectAnswer", -{{#shallowMergeConfig}}[ - { - "container": "#js-answersDirectAnswer" - }, - {{{json componentSettings.DirectAnswer}}} -] -{{/shallowMergeConfig}} -); +{{> templates/common-partials/script/directanswer }} \ No newline at end of file diff --git a/templates/vertical-standard/markup/directanswer.hbs b/templates/vertical-standard/markup/directanswer.hbs new file mode 100644 index 000000000..e22487a72 --- /dev/null +++ b/templates/vertical-standard/markup/directanswer.hbs @@ -0,0 +1 @@ +{{> templates/common-partials/markup/directanswer }} \ No newline at end of file diff --git a/templates/vertical-standard/page-config.json b/templates/vertical-standard/page-config.json index 7286a26f5..b2d9a4bac 100644 --- a/templates/vertical-standard/page-config.json +++ b/templates/vertical-standard/page-config.json @@ -32,6 +32,18 @@ "clearSearchText": "clear search" // Text when there are no results, conducts an empty search }, **/ + /** + "DirectAnswer": { + "types": { + "FEATURED_SNIPPET": { + "cardType": "documentsearch-standard" + }, + "FIELD_VALUE": { + "cardType": "allfields-standard" + } + } + }, + **/ "AppliedFilters": { "removable": true }, diff --git a/templates/vertical-standard/page.html.hbs b/templates/vertical-standard/page.html.hbs index d2a4ee93e..a018e1879 100644 --- a/templates/vertical-standard/page.html.hbs +++ b/templates/vertical-standard/page.html.hbs @@ -1,12 +1,14 @@ {{#> layouts/html }} {{#> script/core }} {{> cards/all }} + {{!-- {{> directanswercards/all }} --}} {{!-- {{> templates/vertical-standard/collapsible-filters/page-setup }} --}} {{> templates/vertical-standard/script/appliedfilters }} {{> templates/vertical-standard/script/verticalresultscount }} {{> templates/vertical-standard/script/searchbar }} {{> templates/vertical-standard/script/spellcheck }} {{> templates/vertical-standard/script/navigation }} + {{!-- {{> templates/vertical-standard/script/directanswer }} --}} {{> templates/vertical-standard/script/verticalresults }} {{> templates/vertical-standard/script/pagination }} {{> templates/vertical-standard/script/locationbias }} @@ -27,6 +29,7 @@ partial for this page, you should not use it on any other page. --> {{!-- {{> layouts/overlay-suggestions }} --}}
+ {{!-- {{> templates/vertical-standard/markup/directanswer }} --}}
{{> templates/vertical-standard/markup/verticalresultscount }} {{> templates/vertical-standard/markup/appliedfilters }} diff --git a/templates/vertical-standard/script/directanswer.hbs b/templates/vertical-standard/script/directanswer.hbs new file mode 100644 index 000000000..b9007a58e --- /dev/null +++ b/templates/vertical-standard/script/directanswer.hbs @@ -0,0 +1 @@ +{{> templates/common-partials/script/directanswer }} \ No newline at end of file diff --git a/tests/templates/script/fixtures/directanswer.hbs.js b/tests/templates/script/fixtures/directanswer.hbs.js new file mode 100644 index 000000000..26566ae1a --- /dev/null +++ b/tests/templates/script/fixtures/directanswer.hbs.js @@ -0,0 +1,2 @@ +ANSWERS.addComponent("DirectAnswer", +{"container":"#js-answersDirectAnswer"}); From 987eb4ed3f4c1031cc1892b1b2747a7e37dbb874 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Thu, 4 Nov 2021 12:55:36 -0400 Subject: [PATCH 02/19] Sandbox/Production environment config (#996) - add commented out `environment` in global_config.json J=SLAP-1663 TEST=manual see the environment is change based on value in global_config.json --- global_config.json | 1 + test-site/config/global_config.json | 1 + 2 files changed, 2 insertions(+) diff --git a/global_config.json b/global_config.json index 9c5aebd9c..862b305ee 100644 --- a/global_config.json +++ b/global_config.json @@ -3,6 +3,7 @@ // "token": "", // The auth token to access Answers experience. // "apiKey": "", // The answers api key found on the experiences page. This will be provided automatically by the Yext CI system // "experienceVersion": "", // the Answers Experience version to use for API requests. This will be provided automatically by the Yext CI system + // "environment": "production", // The environment to run on for this Answers Experience. (i.e. 'production' or 'sandbox') // "businessId": "", // The business ID of the account. This will be provided automatically by the Yext CI system // "initializeManually": true, // If true, the experience must be started by calling AnswersExperience.init() or AnswersExperienceFrame.init() for iframe integrations. // "useJWT": true, // Whether or not to enable JWT. If true, the apiKey will be hidden from the build output and the token must be specified through manual initialization. diff --git a/test-site/config/global_config.json b/test-site/config/global_config.json index 571bc24fc..06eca7b87 100644 --- a/test-site/config/global_config.json +++ b/test-site/config/global_config.json @@ -3,6 +3,7 @@ // "token": "", // The auth token to access Answers experience. "apiKey": "2d8c550071a64ea23e263118a2b0680b", // The answers api key found on the experiences page. This will be provided automatically by the Yext CI system // "experienceVersion": "", // the Answers Experience version to use for API requests. This will be provided automatically by the Yext CI system + // "environment": "production", // The environment to run on for this Answers Experience. (i.e. 'production' or 'sandbox') // "businessId": "", // The business ID of the account. This will be provided automatically by the Yext CI system // "initializeManually": true, // If true, the experience must be started by calling AnswersExperience.init() or AnswersExperienceFrame.init() for iframe integrations. // "useJWT": true, // Whether or not to enable JWT. If true, the apiKey will be hidden from the build and the token must be specified through the runtime config. From 100c8d0fa41147f7758477558931e28da07bdb53 Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Fri, 5 Nov 2021 10:41:49 -0400 Subject: [PATCH 03/19] move on-document-load after ManualInitializer is instantiated (#995) This allows users to call window.AnswersExperience.init() from within the on-document-load partial. I added an error message if you try to call window.AnswersExperience.init() before ManualInitializer.setup() has been called. While testing, I noticed a bug with our script/* partials that get inserted into inline js. If the last line of one of these partials is a single line js comment, then the line immediately following the partial in the template would also be commented out i.e., the below template ```hbs {{#*inline 'script-partial'}}// I will comment out the line after me too{{/inline}} function () { {{> script-partial }} } ``` gets the output ```js function () { // I will comment out the line after me too} ``` You can easily test this out in the handlebars playground or in a local node script. To fix this I added new lines to the end of script/* partials that would be affected by this. J=SLAP-1673 TEST=manual tested calling window.AnswersExperience.init inside on-document-load with initializeManually set to true/false, and doing an end-to-end with consumer auth --- hbshelpers/wrapJsPartial.js | 15 +++++++++++++++ layouts/html.hbs | 11 +++++++++-- script/core.hbs | 12 +++++++++--- static/js/answers-experience.js | 4 ++++ static/js/manual-initializer.js | 3 ++- 5 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 hbshelpers/wrapJsPartial.js diff --git a/hbshelpers/wrapJsPartial.js b/hbshelpers/wrapJsPartial.js new file mode 100644 index 000000000..42df79482 --- /dev/null +++ b/hbshelpers/wrapJsPartial.js @@ -0,0 +1,15 @@ +/** + * This is a block helper for injecting inline JS using handlebars. + * + * First, it wraps the statement in an iife to prevent namespacing issues. + * + * Then, it adds an extra new line to the end of the iife. + * This is necesssary because handlebars removes the first new line it sees after a partial invocation, + * which can result in certain js parsing issues, for example if the last line of the partial is a + * single line js comment, the line immediately after the partial will be included in the comment. + * + * @param {import('handlebars').HelperOptions} opts + */ +module.exports = function wrapJsPartial(opts) { + return `(() => { \n${opts.fn()}\n })()\n`; +} \ No newline at end of file diff --git a/layouts/html.hbs b/layouts/html.hbs index 2abe7f518..ad1446197 100644 --- a/layouts/html.hbs +++ b/layouts/html.hbs @@ -6,7 +6,9 @@ if (window.name == 'overlay') { window.isOverlay = true; } - {{> script/translations}} + {{#wrapJsPartial}} + {{> script/translations}} + {{/wrapJsPartial}} {{/babel}} {{> layouts/headincludes }} @@ -72,10 +74,15 @@ diff --git a/static/js/answers-experience.js b/static/js/answers-experience.js index 9b528ac0f..9e4450cce 100644 --- a/static/js/answers-experience.js +++ b/static/js/answers-experience.js @@ -22,6 +22,10 @@ export default class AnswersExperience { this._registerRuntimeConfigListeners(); } + init() { + console.error('AnswersExperience.init was called before an init method was set. This is a no-op.') + } + /** * Registers runtime config listeners and ensures that they execute * after Answers has initialized diff --git a/static/js/manual-initializer.js b/static/js/manual-initializer.js index 7776ab547..aa25d6d43 100644 --- a/static/js/manual-initializer.js +++ b/static/js/manual-initializer.js @@ -1,5 +1,6 @@ /** - * Responsible for setting up manual initialization of the experience + * Responsible for setting up manual initialization of the experience. + * Sets the "init" method of the {@link AnswersExperience} on the window. */ export default class ManualInitializer { /** From 26f5579a4ccf143ba1a9feaaad194a06c2796246 Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Fri, 5 Nov 2021 12:51:19 -0400 Subject: [PATCH 04/19] set default cta linkType to URL (#997) The API is allowed to send an undefined cta linkType. Setting the default to URL effectively grabs the link with no extra formatting. J=SLAP-1655 TEST=auto --- static/js/formatters/generate-cta-field-type-link.js | 5 ++++- .../formatters-internal/generate-cta-field-type-link.js | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/static/js/formatters/generate-cta-field-type-link.js b/static/js/formatters/generate-cta-field-type-link.js index 8b8ce6aec..03710d452 100644 --- a/static/js/formatters/generate-cta-field-type-link.js +++ b/static/js/formatters/generate-cta-field-type-link.js @@ -1,6 +1,9 @@ import CtaFormatter from '@yext/cta-formatter'; /** + * By default, the linkType is assumed to be 'URL', which does not apply additional formatting, as opposed + * to the "Phone" and "Email" linkTypes. + * * @param {{link: string, linkType: string}} cta the Calls To Action field object * @returns {string} The formatted url associated with the Call to Action object if the cta object exists, null otherwise */ @@ -10,7 +13,7 @@ export function generateCTAFieldTypeLink(cta) { } const normalizedCTA = { ...cta, - linkType: normalizeCtaLinkType(cta.linkType) + linkType: normalizeCtaLinkType(cta.linkType || 'URL') } return CtaFormatter.generateCTAFieldTypeLink(normalizedCTA); } diff --git a/tests/static/js/formatters-internal/generate-cta-field-type-link.js b/tests/static/js/formatters-internal/generate-cta-field-type-link.js index 3de5a33ab..2d7a753ac 100644 --- a/tests/static/js/formatters-internal/generate-cta-field-type-link.js +++ b/tests/static/js/formatters-internal/generate-cta-field-type-link.js @@ -25,4 +25,11 @@ describe('generateCtaFieldTypeLinks can handle translated link types', () => { } expect(generateCTAFieldTypeLink(cta)).toEqual('slap'); }); -}); \ No newline at end of file +}); + +it('works with no linkType set', () => { + const cta = { + link: 'slap' + } + expect(generateCTAFieldTypeLink(cta)).toEqual('slap'); +}) \ No newline at end of file From a89f0966969703eb6879a1df1bd6b3fe201c1604 Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Tue, 9 Nov 2021 14:14:08 -0500 Subject: [PATCH 05/19] Update image formatter to use dynamic thumbnailer (#998) Change the image formatter to use dynamic thumbnailer instead of expecting an array of thumbnails. A thumbnail array will no longer be provided after the streams migration. Dynamic thumbnailer uses the host `dynl` for images at least as large as the specified size (the smallest image with at least one dimension greater than or equal to the specified values). The host `dynm` gives the largest image that is smaller than the specified values in both dimensions. Note: for `dynl`, desired size values of 1 or smaller are a special case which indicates that dimension should be ignored and the returned thumbnail should be larger than the other dimension. J=SLAP-1670 TEST=auto, manual Check that the current profile images still appear as before and those in the streams test account now appear as well. --- static/js/formatters-internal.js | 175 ++++++++----------- tests/static/js/formatters-internal/image.js | 72 ++------ 2 files changed, 88 insertions(+), 159 deletions(-) diff --git a/static/js/formatters-internal.js b/static/js/formatters-internal.js index a468ec024..5e4137afe 100644 --- a/static/js/formatters-internal.js +++ b/static/js/formatters-internal.js @@ -281,134 +281,107 @@ export function joinList(list, separator) { return list.join(separator); } -/* - * Given object with url and alternateText, changes url to use https +/** + * Given an image object with a url, changes the url to use dynamic thumbnailer and https. + * + * Note: A dynamic thumbnailer url generated with atLeastAsLarge = true returns an image that is + * at least as large in one dimension of the desired size. In other words, the returned image will + * be at least as large, and as close as possible to, the largest image that is contained within a + * box of the desired size dimensions. + * + * If atLeastAsLarge = false, the dynamic thumbnailer url will give the largest image that is + * smaller than the desired size in both dimensions. + * + * @param {Object} simpleOrComplexImage An image object with a url + * @param {string} desiredSize The desired size of the image ('x') + * @param {boolean} atLeastAsLarge Whether the image should be at least as large as the desired + * size in one dimension or smaller than the desired size in both + * dimensions. + * @returns {Object} An object with a url for dynamic thumbnailer */ -export function image(simpleOrComplexImage = {}, size = '200x', atLeastAsLarge = true) { - let img = simpleOrComplexImage.image || simpleOrComplexImage; - if (!img) { +export function image(simpleOrComplexImage = {}, desiredSize = '200x', atLeastAsLarge = true) { + let image = simpleOrComplexImage.image || simpleOrComplexImage; + if (!image) { return {}; } - - if (!img.url) { - return img; + if (!image.url) { + return image; + } + if (!(Object.prototype.toString.call(image).indexOf('Object') > 0)) { + throw new Error("Expected parameter of type Map"); + } + if ((typeof desiredSize !== 'string') || (desiredSize == null)) { + throw new Error(`Object of type string expected. Got ${typeof desiredSize}.`); + } + if (desiredSize.indexOf('x') === -1) { + throw new Error("Invalid desired size"); + } + if ((typeof atLeastAsLarge !== 'boolean') || (atLeastAsLarge == null)) { + throw new Error(`Object of type boolean expected. Got ${typeof atLeastAsLarge}.`); } - function imageBySizeEntity(image, desiredSize, atLeastAsLarge = true) { - if ((image == null) || !(Object.prototype.toString.call(image).indexOf('Object') > 0)) { - throw new Error("Expected parameter of type Map"); - } - if ((typeof desiredSize !== 'string') || (desiredSize == null)) { - throw new Error(`Object of type string expected. Got ${typeof desiredSize}.`); - } - if (desiredSize.indexOf('x') === -1) { - throw new Error("Invalid desired size"); - } - if ((typeof atLeastAsLarge !== 'boolean') || (atLeastAsLarge == null)) { - throw new Error(`Object of type boolean expected. Got ${typeof atLeastAsLarge}.`); - } - - if (!image.thumbnails) { - image.thumbnails = []; - } - - if (!Array.isArray(image.thumbnails)) { - throw new Error(`Object of type array expected. Got ${typeof image.thumbnails}.`); - } - - if (image.width != undefined && image.height != undefined && image.url != undefined) { - image.thumbnails.push({ - 'width': image.width, - 'height': image.height, - 'url': image.url - }); - } + let desiredWidth, desiredHeight; + let desiredDims = desiredSize.split('x'); - let desiredWidth, desiredHeight; - let desiredDims = desiredSize.split('x'); + const [urlWithoutExtension, extension] = _splitStringOnIndex(image.url, image.url.lastIndexOf('.')); + const [urlBeforeDimensions, dimensions] = _splitStringOnIndex(urlWithoutExtension, urlWithoutExtension.lastIndexOf('/') + 1); + const fullSizeDims = dimensions.split('x'); - if (desiredDims[0] !== '') { - desiredWidth = Number.parseInt(desiredDims[0]); - if (Number.isNaN(desiredWidth)) { - throw new Error("Invalid width specified"); - } + if (desiredDims[0] !== '') { + desiredWidth = Number.parseInt(desiredDims[0]); + if (Number.isNaN(desiredWidth)) { + throw new Error("Invalid width specified"); } + } else { + desiredWidth = atLeastAsLarge ? 1 : Number.parseInt(fullSizeDims[0]); + } - if (desiredDims[1] !== '') { - desiredHeight = Number.parseInt(desiredDims[1]); - if (Number.isNaN(desiredHeight)) { - throw new Error("Invalid height specified"); - } + if (desiredDims[1] !== '') { + desiredHeight = Number.parseInt(desiredDims[1]); + if (Number.isNaN(desiredHeight)) { + throw new Error("Invalid height specified"); } - const thumbnails = image.thumbnails - .filter(thumb => thumb.width && thumb.height) - .sort((a, b) => b.width - a.width); - return atLeastAsLarge - ? _getSmallestThumbnailOverThreshold(thumbnails, desiredWidth, desiredHeight) - : _getLargestThumbnailUnderThreshold(thumbnails, desiredWidth, desiredHeight); + } else { + desiredHeight = atLeastAsLarge ? 1 : Number.parseInt(fullSizeDims[1]); } - const result = imageBySizeEntity(img, size, atLeastAsLarge); + const urlWithDesiredDims = urlBeforeDimensions + desiredWidth + 'x' + desiredHeight + extension; + + const dynamicUrl = atLeastAsLarge + ? _replaceUrlHost(urlWithDesiredDims, 'dynl.mktgcdn.com') + : _replaceUrlHost(urlWithDesiredDims, 'dynm.mktgcdn.com'); return Object.assign( {}, - img, + image, { - url: result.replace('http://', 'https://') + url: dynamicUrl.replace('http://', 'https://') } ); } /** - * Gets the smallest thumbnail that is over the min width and min height. - * If no thumbnails are over the given thresholds, will return the closest one. - * - * This method assumes all thumbnails have the same aspect ratio, and that - * thumbnails are sorted in descending size. - * - * @param {Array<{{url: string, width: number, height: number}}>} thumbnails - * @param {number|undefined} minWidth - * @param {number|undefined} minHeight - * @returns {string} + * Splits a string into two parts at the specified index. + * + * @param {string} str The string to be split + * @param {number} index The index at which to split the string + * @returns {Array} The two parts of the string after splitting */ -function _getSmallestThumbnailOverThreshold(thumbnails, minWidth, minHeight) { - let index = thumbnails.length - 1; - while (index > 0) { - const thumb = thumbnails[index]; - const widthOverThreshold = minWidth ? thumb.width >= minWidth : true; - const heightOverThreshold = minHeight ? thumb.height >= minHeight : true; - if (widthOverThreshold && heightOverThreshold) { - return thumb.url - } - index--; - } - return thumbnails[0].url; +function _splitStringOnIndex(str, index) { + return [str.slice(0, index), str.slice(index)]; } /** - * Gets the largest thumbnail that is under the max width and max height. - * If no thumbnails are under the given thresholds, will return the closest one. + * Replaces the current host of a url with the specified host. * - * This method assumes all thumbnails have the same aspect ratio, and that - * thumbnails are sorted in descending size. - * - * @param {Array<{{url: string, width: number, height: number}}>} thumbnails - * @param {number|undefined} maxWidth - * @param {number|undefined} maxHeight - * @returns {string} + * @param {string} url The url whose host is to be changed + * @param {string} host The new host to change to + * @returns {string} The url updated with the specified host */ -function _getLargestThumbnailUnderThreshold(thumbnails, maxWidth, maxHeight) { - let index = 0; - while (index < thumbnails.length) { - const thumb = thumbnails[index]; - const widthOverThreshold = maxWidth ? thumb.width <= maxWidth : true; - const heightOverThreshold = maxHeight ? thumb.height <= maxHeight : true; - if (widthOverThreshold && heightOverThreshold) { - return thumb.url - } - index++; - } - return thumbnails[thumbnails.length - 1].url; +function _replaceUrlHost(url, host) { + const splitUrl = url.split('://'); + const urlAfterHost = splitUrl[1].slice(splitUrl[1].indexOf('/')); + return splitUrl[0] + '://' + host + urlAfterHost; } /** diff --git a/tests/static/js/formatters-internal/image.js b/tests/static/js/formatters-internal/image.js index 01996bbb1..c714334d9 100644 --- a/tests/static/js/formatters-internal/image.js +++ b/tests/static/js/formatters-internal/image.js @@ -2,99 +2,55 @@ import Formatters from 'static/js/formatters.js'; describe('image formatter', () => { const img = { - url: 'https://a.mktgcdn.com/p/1024x768.jpg', - width: 1024, - height: 768, - thumbnails: [ - { - url: 'https://a.mktgcdn.com/p/619x348.jpg', - width: 619, - height: 348 - }, - { - url: 'https://a.mktgcdn.com/p/600x337.jpg', - width: 600, - height: 337 - }, - { - url: 'https://a.mktgcdn.com/p/196x110.jpg', - width: 196, - height: 110 - } - ] + url: 'https://a.mktgcdn.com/p/1024x768.jpg' } describe('when choosing the smallest image over threshold', () => { it('By default chooses the smallest image with width >= 200', () => { const imageUrl = Formatters.image(img).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/600x337.jpg'); + expect(imageUrl).toEqual('https://dynl.mktgcdn.com/p/200x1.jpg'); }); it('Can restrict the dimensions by width', () => { const imageUrl = Formatters.image(img, '601x').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/619x348.jpg'); - }); - - it('Can restrict by width when both dimensions specified', () => { - const imageUrl = Formatters.image(img, '601x1').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/619x348.jpg'); + expect(imageUrl).toEqual('https://dynl.mktgcdn.com/p/601x1.jpg'); }); it('Can restrict the dimensions by height', () => { const imageUrl = Formatters.image(img, 'x338').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/619x348.jpg'); - }); - - it('Can restrict by height when both dimensions specified', () => { - const imageUrl = Formatters.image(img, '1x338').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/619x348.jpg'); - }); - - it('Can restrict by width when both dimensions specified', () => { - const imageUrl = Formatters.image(img, '601x0').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/619x348.jpg'); + expect(imageUrl).toEqual('https://dynl.mktgcdn.com/p/1x338.jpg'); }); - it('return the largest image when no image is over threshold', () => { - const imageUrl = Formatters.image(img, '99999x99999').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/1024x768.jpg'); + it('Can restrict by both dimensions', () => { + const imageUrl = Formatters.image(img, '601x338').url; + expect(imageUrl).toEqual('https://dynl.mktgcdn.com/p/601x338.jpg'); }); it('returns the smallest image when no dimensions given', () => { const imageUrl = Formatters.image(img, 'x').url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/196x110.jpg'); + expect(imageUrl).toEqual('https://dynl.mktgcdn.com/p/1x1.jpg'); }); }); describe('when choosing the biggest image under threshold', () => { it('Can restrict the dimensions by width', () => { const imageUrl = Formatters.image(img, '601x', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/600x337.jpg'); - }); - - it('Can restrict by width when both dimensions specified', () => { - const imageUrl = Formatters.image(img, '601x9999', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/600x337.jpg'); + expect(imageUrl).toEqual('https://dynm.mktgcdn.com/p/601x768.jpg'); }); it('Can restrict the dimensions by height', () => { const imageUrl = Formatters.image(img, 'x338', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/600x337.jpg'); - }); - - it('Can restrict by height when both dimensions specified', () => { - const imageUrl = Formatters.image(img, '9999x338', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/600x337.jpg'); + expect(imageUrl).toEqual('https://dynm.mktgcdn.com/p/1024x338.jpg'); }); - it('returns the smallest image when no image is under threshold', () => { - const imageUrl = Formatters.image(img, '-1x-1', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/196x110.jpg'); + it('Can restrict by both dimensions', () => { + const imageUrl = Formatters.image(img, '999x338', false).url; + expect(imageUrl).toEqual('https://dynm.mktgcdn.com/p/999x338.jpg'); }); it('return the largest image when no dimensions given', () => { const imageUrl = Formatters.image(img, 'x', false).url; - expect(imageUrl).toEqual('https://a.mktgcdn.com/p/1024x768.jpg'); + expect(imageUrl).toEqual('https://dynm.mktgcdn.com/p/1024x768.jpg'); }); }); }); \ No newline at end of file From bbb7480ddb7ec7c95de5331bb389ca4f1f7169c9 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Wed, 10 Nov 2021 14:01:04 -0500 Subject: [PATCH 06/19] CaC Percy and WCaG testing infrastructure (#999) Update testing infrastructure to CaC approach for percy and wcag tests. - added `PageOperator` which is responsible fo automating browser navigation using the given test locations - test locations provide information on what page to navigate to and what action/query to perform on that page - update wcag and percy index.js file to use PageOperator - added some snapshots for es/ar pages that wasn't there before - remove photographer, multilangphotographer, and wcagreporter class as they are no longer needed with pageOperator in use J=1657 TEST=auto see that wcag tests run successfully on local machine see that percy snapshots are taken properly in this pr --- package.json | 1 + .../constants.js | 2 +- .../multilangqueries.json} | 0 tests/browser-automation/pageoperator.js | 59 +++++ tests/browser-automation/testlocations.js | 210 ++++++++++++++++++ tests/percy/camera.js | 2 +- tests/percy/index.js | 27 ++- tests/percy/multilangphotographer.js | 129 ----------- tests/percy/photographer.js | 143 ------------ tests/wcag/index.js | 11 +- tests/wcag/wcagreporter.js | 147 ------------ 11 files changed, 300 insertions(+), 431 deletions(-) rename tests/{percy => browser-automation}/constants.js (97%) rename tests/{percy/queries.json => browser-automation/multilangqueries.json} (100%) create mode 100644 tests/browser-automation/pageoperator.js create mode 100644 tests/browser-automation/testlocations.js delete mode 100644 tests/percy/multilangphotographer.js delete mode 100644 tests/percy/photographer.js delete mode 100644 tests/wcag/wcagreporter.js diff --git a/package.json b/package.json index 17bfe7a83..3f156692d 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "/fixtures/", "/test-utils/", "/test-site/", + "/browser-automation/", "/percy/", "/wcag/", "/acceptance/" diff --git a/tests/percy/constants.js b/tests/browser-automation/constants.js similarity index 97% rename from tests/percy/constants.js rename to tests/browser-automation/constants.js index 07c10182a..fabc05622 100644 --- a/tests/percy/constants.js +++ b/tests/browser-automation/constants.js @@ -1,4 +1,4 @@ module.exports.BrowserPageWidths = { Desktop: 1280, Mobile: 375 -} \ No newline at end of file +} diff --git a/tests/percy/queries.json b/tests/browser-automation/multilangqueries.json similarity index 100% rename from tests/percy/queries.json rename to tests/browser-automation/multilangqueries.json diff --git a/tests/browser-automation/pageoperator.js b/tests/browser-automation/pageoperator.js new file mode 100644 index 000000000..4ca0cb80f --- /dev/null +++ b/tests/browser-automation/pageoperator.js @@ -0,0 +1,59 @@ +const { BrowserPageWidths } = require("./constants"); + +/** + * Responsible for browser navigation based on given test locations. + * Each test location is an object which may contain: + * - name: name of this test + * - page: the vertical page to navigate to (if omit, navigate to universal page by default) + * - queryParams: the query params to peform the search on + * - viewport: viewport of the page (if omit, desktop view is used by default) + * - commands: a list of actions to perform on the page by the page navigator + */ +class PageOperator { + /** + * @param {PageNavigator} pageNavigator page navigator + * @param {import('puppeteer').Page} page A Pupeteer Page + * @param {Object[]} testLocations a list of test locations to navigate to + */ + constructor(pageNavigator, page, testLocations) { + this._pageNavigator = pageNavigator; + this._page = page; + this._testLocations = testLocations; + this._testLocationIndex = -1; + } + + hasNextTestLocation() { + return this._testLocationIndex < this._testLocations.length - 1; + } + + async nextTestLocation() { + this._testLocationIndex++; + const testConfig = this._testLocations[this._testLocationIndex]; + await this._setPageViewport(testConfig.viewport); + testConfig.page + ? await this._pageNavigator.gotoVerticalPage(testConfig.page, testConfig.queryParams) + : await this._pageNavigator.gotoUniversalPage(testConfig.queryParams); + await this._executeTestCommands(testConfig.commands); + return testConfig; + } + + async _setPageViewport(viewport) { + if (viewport && !['desktop', 'mobile'].includes(viewport)) { + throw error(`unknown viewport: ${viewport}`); + } + viewport === 'mobile' + ? await this._page.setViewport({ width: BrowserPageWidths.Mobile, height: this._page.viewport().height }) + : await this._page.setViewport({ width: BrowserPageWidths.Desktop, height: this._page.viewport().height }); + } + + async _executeTestCommands(commands) { + if (!commands) { + return; + } + for (const command of commands ) { + await this._pageNavigator[command.type].call(this._pageNavigator, command.params); + } + } +} + +module.exports = PageOperator; diff --git a/tests/browser-automation/testlocations.js b/tests/browser-automation/testlocations.js new file mode 100644 index 000000000..3141c24b2 --- /dev/null +++ b/tests/browser-automation/testlocations.js @@ -0,0 +1,210 @@ +const queryConfig = require('./multilangqueries.json'); + +const universalSearchTests = (multilangQueries) => [ + { + name: 'universal-search', + }, + { + name: 'universal-search--no-results', + queryParams: { query: 'a' } + }, + { + name: 'universal-search--spellcheck', + queryParams: { query: 'office sparce' } + }, + { + name: 'universal-search--faq-accordion', + queryParams: { query: multilangQueries.faq }, + commands: [{ type: 'click', params: ['.HitchhikerFaqAccordion-toggle'] }] + }, + { + name: 'universal-search--product-prominentimage', + queryParams: { query: 'yext answers' } + } +]; + +const verticalSearchTests = (multilangQueries) => [ + { + name: 'vertical-search', + page: 'events' + }, + { + name: 'vertical-search--no-results', + page: 'events', + queryParams: { query: 'a' } + }, + { + name: 'vertical-search--spellcheck', + page: 'events', + queryParams: { query: 'vrginia' } + }, + { + name: 'vertical-search--custom-cta-icons', + page: 'events_custom_cta_icons' + }, + { + name: 'vertical-search--financial-professional-location', + page: 'financial_professionals', + queryParams: { query: 'connor' } + }, + { + name: 'vertical-search--healthcare-professional-location', + page: 'healthcare_professionals', + queryParams: { query: 'bob' } + }, + { + name: 'vertical-search--job-standard', + page: 'jobs', + queryParams: { query: multilangQueries.job } + }, + { + name: 'vertical-search--document-standard', + page: 'help_articles', + queryParams: { query: 'slap chop' } + }, + { + name: 'vertical-search--menuitem-standard', + page: 'menu_items', + queryParams: { query: multilangQueries.menu_item } + } +]; + +const verticalGridSearchTests = () => [ + { + name: 'vertical-grid-search', + page: 'people', + queryParams: { query: 'a' } + }, + { + name: 'vertical-grid-search--spellcheck', + page: 'people', + queryParams: { query: 'vrginia' } + }, + { + name: 'vertical-grid-search--product-prominentvideo', + page: 'products', + queryParams: { query: 'yext answers' } + }, + { + name: 'vertical-grid-search--product-prominentimage-clickable', + page: 'products_clickable_image', + queryParams: { query: 'yext answers' } + } +]; + +const verticalMapSearchTests = () => [ + { + name: 'vertical-map-search', + page: 'locations', + queryParams: { query: 'a' } + }, + { + name: 'vertical-map-search--google', + page: 'locations_google', + queryParams: { query: 'virginia' } + } +]; + +const verticalFullPageMapSearchTests = () => [ + { + name: 'vertical-full-page-map__desktop-view', + page: 'locations_full_page_map', + queryParams: { query: '' }, + viewport: 'desktop' + }, + { + name: 'vertical-full-page-map__mobile-list-view', + page: 'locations_full_page_map', + queryParams: { query: '' }, + viewport: 'mobile' + }, + { + name: 'vertical-full-page-map__mobile-map-view', + page: 'locations_full_page_map', + queryParams: { query: '' }, + viewport: 'mobile', + commands: [{ type: 'click', params: ['.Answers-mobileToggle'] }] + }, + { + name: 'vertical-full-page-map__mobile-detail-view', + page: 'locations_full_page_map', + queryParams: { query: '' }, + viewport: 'mobile', + commands: [ + { type: 'click', params: ['.Answers-mobileToggle'] }, + { type: 'click', params: ['.js-answersMap button'] } + ] + }, + { + name: 'vertical-full-page-map--alternative-verticals', + page: 'locations_full_page_map_with_filters', + queryParams: { query: 'people' } + }, + { + name: 'vertical-full-page-map--spellcheck__desktop-view', + page: 'locations_full_page_map', + queryParams: { query: 'office sparce' }, + viewport: 'desktop' + }, + { + name: 'vertical-full-page-map--spellcheck__mobile-list-view', + page: 'locations_full_page_map', + queryParams: { query: 'office sparce' }, + viewport: 'mobile' + }, + { + name: 'vertical-full-page-map--nlp-filters__desktop-view', + page: 'locations_full_page_map', + queryParams: { query: 'virginia' }, + viewport: 'desktop' + }, + { + name: 'vertical-full-page-map-with-filters--nlp-filters__desktop-view', + page: 'locations_full_page_map_with_filters', + queryParams: { query: 'virginia' }, + viewport: 'desktop' + } +]; + +const directAnswersTests = (multilangQueries) => [ + { + name: 'field-direct-answer', + queryParams: { query: multilangQueries.field_direct_answers } + }, + { + name: 'documentsearch-direct-answer', + queryParams: { query: 'where was joe exotic born?' } + }, + { + name: 'documentsearch-rich-text-direct-answer', + queryParams: { query: 'how to get rich text' } + }, + { + name: 'documentsearch-rich-text-picture-direct-answer', + queryParams: { query: 'who is howard?' } + } +]; + +const multilangDirectAnswersTests = (multilangQueries) => [ + { + name: 'field-direct-answer', + queryParams: { query: multilangQueries.field_direct_answers } + } +]; + +/** + * Generate test locations based on the given locale. + * + * @param {string} locale locale of the page + * @returns an array of test locations + */ +const getTestingLocations = (locale='en') => [ + ...universalSearchTests(queryConfig[locale]), + ...verticalSearchTests(queryConfig[locale]), + ...verticalGridSearchTests(), + ...verticalMapSearchTests(), + ...verticalFullPageMapSearchTests(), + ...(locale === 'en' ? directAnswersTests(queryConfig[locale]) : multilangDirectAnswersTests(queryConfig[locale])) +] + +module.exports = getTestingLocations; diff --git a/tests/percy/camera.js b/tests/percy/camera.js index 12946c3c5..6ffd013a2 100644 --- a/tests/percy/camera.js +++ b/tests/percy/camera.js @@ -1,4 +1,4 @@ -const { BrowserPageWidths } = require('./constants'); +const { BrowserPageWidths } = require('../browser-automation/constants'); /** * Responsible for taking Percy snapshots diff --git a/tests/percy/index.js b/tests/percy/index.js index 952fc79a2..73d4f4b68 100644 --- a/tests/percy/index.js +++ b/tests/percy/index.js @@ -1,24 +1,23 @@ const HttpServer = require('../test-utils/server'); -const Photographer = require('./photographer'); -const MultilangPhotographer = require('./multilangphotographer'); const StandardPageNavigator = require('./standardpagenavigator'); const IframePageNavigator = require('./iframepagenavigator'); const Camera = require('./camera'); -const queryConfig = require('./queries.json'); const puppeteer = require('puppeteer'); const percySnapshot = require('@percy/puppeteer'); +const PageOperator = require('../browser-automation/pageoperator'); +const getTestingLocations = require('../browser-automation/testlocations'); const PORT = 5042; async function defaultSnapshots(page) { const standardPageNavigator = new StandardPageNavigator(page, `http://localhost:${PORT}`); const standardCamera = new Camera(percySnapshot, page); - await (new Photographer(standardPageNavigator, standardCamera).captureSnapshots()); + await captureSnapshots(standardPageNavigator, page, standardCamera); } async function iframeSnapshots(page) { const iframePageNavigator = new IframePageNavigator(page, `http://localhost:${PORT}`, 'iframe_test'); const iframeCamera = new Camera(percySnapshot, page, true); - await (new Photographer(iframePageNavigator, iframeCamera).captureSnapshots()); + await captureSnapshots(iframePageNavigator, page, iframeCamera); } async function spanishSnapshots(page) { @@ -26,7 +25,7 @@ async function spanishSnapshots(page) { const standardCamera = new Camera(percySnapshot, page); standardPageNavigator.setCurrentLocale('es'); standardCamera.setLocale('es'); - await (new MultilangPhotographer(standardPageNavigator, standardCamera, queryConfig.es).captureSnapshots()); + await captureSnapshots(standardPageNavigator, page, standardCamera, 'es'); } async function rtlSnapshots(page) { @@ -34,7 +33,21 @@ async function rtlSnapshots(page) { const standardCamera = new Camera(percySnapshot, page); standardPageNavigator.setCurrentLocale('ar'); standardCamera.setLocale('ar'); - await (new MultilangPhotographer(standardPageNavigator, standardCamera, queryConfig.ar).captureSnapshots()); + await captureSnapshots(standardPageNavigator, page, standardCamera, 'ar'); +} + +async function captureSnapshots(navigator, page, camera, locale='en') { + const operator = new PageOperator(navigator, page, getTestingLocations(locale)); + while (operator.hasNextTestLocation()) { + const testConfig = await operator.nextTestLocation(); + if (testConfig.viewport) { + testConfig.viewport === 'mobile' + ? await camera.snapshotMobileOnly(testConfig.name) + : await camera.snapshotDesktopOnly(testConfig.name); + } else { + await camera.snapshot(testConfig.name); + } + } } async function runPercyTest() { diff --git a/tests/percy/multilangphotographer.js b/tests/percy/multilangphotographer.js deleted file mode 100644 index 65bf8829e..000000000 --- a/tests/percy/multilangphotographer.js +++ /dev/null @@ -1,129 +0,0 @@ -/** - * @typedef {import('./pagenavigator.js')} PageNavigator - * @typedef {import('./Camera.js')} Camera - */ - -/** - * Responsible for determining which snapshots to take for specific locale - */ -class MultilangPhotographer { - /** - * @param {PageNavigator} pageNavigator - * @param {Camera} camera - * @param {Object} queries custom queries for specific locale - */ - constructor(pageNavigator, camera, queries) { - this._pageNavigator = pageNavigator; - this._camera = camera; - this._queries = queries; - } - - /** - * Sets custom queries based on locale - * - * @param {Object} queries - */ - setLocaleQueries(queries) { - this._queries = queries; - } - - async captureSnapshots() { - await this._captureUniversalSearch(); - await this._captureVerticalSearch(); - await this._captureVerticalGridSearch(); - await this._captureVerticalMapSearch(); - await this._captureVerticalFullPageMapSearch(); - await this._captureDirectAnswers(); - } - - async _captureUniversalSearch () { - await this._pageNavigator.gotoUniversalPage(); - await this._camera.snapshot('universal-search'); - - await this._pageNavigator.gotoUniversalPage({ query: 'a' }); - await this._camera.snapshot('universal-search--no-results'); - - await this._pageNavigator.gotoUniversalPage({ query: this._queries.faq }); - await this._pageNavigator.click('.HitchhikerFaqAccordion-toggle') - await this._camera.snapshot('universal-search--faq-accordion'); - - await this._pageNavigator.gotoUniversalPage({ query: 'yext answers'}); - await this._camera.snapshot('universal-search--product-prominentimage'); - } - - async _captureVerticalSearch () { - await this._pageNavigator.gotoVerticalPage('events'); - await this._camera.snapshot('vertical-search'); - - await this._pageNavigator.gotoVerticalPage('events', { query: 'a' }); - await this._camera.snapshot('vertical-search--no-results'); - - await this._pageNavigator.gotoVerticalPage('financial_professionals', { query: 'connor' }); - await this._camera.snapshot('vertical-search--financial-professional-location'); - - await this._pageNavigator.gotoVerticalPage('jobs', { query: this._queries.job }); - await this._camera.snapshot('vertical-search--job-standard'); - - await this._pageNavigator.gotoVerticalPage('help_articles', { query: 'slap chop' }); - await this._camera.snapshot('vertical-search--document-standard'); - - await this._pageNavigator.gotoVerticalPage('menu_items', { query: this._queries.menu_item }); - await this._camera.snapshot('vertical-search--menuitem-standard'); - } - - async _captureVerticalGridSearch () { - await this._pageNavigator.gotoVerticalPage('people', { query: 'a' }); - await this._camera.snapshot('vertical-grid-search'); - - await this._pageNavigator.gotoVerticalPage('people', { query: 'vrginia' }); - await this._camera.snapshot('vertical-grid-search--spellcheck'); - - await this._pageNavigator.gotoVerticalPage('products', { query: 'yext answers' }); - await this._camera.snapshot('vertical-grid-search--product-prominentvideo'); - - await this._pageNavigator.gotoVerticalPage('products_clickable_image', { query: 'yext answers' }); - await this._camera.snapshot('vertical-grid-search--product-prominentimage-clickable'); - } - - async _captureVerticalMapSearch () { - await this._pageNavigator.gotoVerticalPage('locations', { query: 'a' }); - await this._camera.snapshot('vertical-map-search'); - - await this._pageNavigator.gotoVerticalPage('locations_google', { query: 'virginia' }); - await this._camera.snapshot('vertical-map-search--google'); - } - - async _captureVerticalFullPageMapSearch () { - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: '' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map__desktop-view'); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-list-view'); - - await this._pageNavigator.click('.Answers-mobileToggle'); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-map-view'); - - const mapboxPinSelector = '.js-answersMap button'; - await this._pageNavigator.click(mapboxPinSelector); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-detail-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'office sparce'}); - await this._camera.snapshotDesktopOnly('vertical-full-page-map--spellcheck__desktop-view'); - await this._camera.snapshotMobileOnly('vertical-full-page-map--spellcheck__mobile-list-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'virginia' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map--nlp-filters__desktop-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map_with_filters', { query: 'virginia' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map-with-filters--nlp-filters__desktop-view'); - } - - async _captureDirectAnswers () { - await this._pageNavigator.gotoUniversalPage({ query: this._queries.field_direct_answers }); - await this._camera.snapshot('field-direct-answer'); - } -} - -module.exports = MultilangPhotographer; diff --git a/tests/percy/photographer.js b/tests/percy/photographer.js deleted file mode 100644 index 3ad306f20..000000000 --- a/tests/percy/photographer.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * @typedef {import('./pagenavigator.js')} PageNavigator - * @typedef {import('./Camera.js')} Camera - */ - -/** - * Responsible for determining which snapshots to take - */ -class Photographer { - /** - * @param {PageNavigator} pageNavigator - * @param {Camera} camera - */ - constructor(pageNavigator, camera) { - this._pageNavigator = pageNavigator; - this._camera = camera; - } - - async captureSnapshots() { - await this._captureUniversalSearch(); - await this._captureVerticalSearch(); - await this._captureVerticalGridSearch(); - await this._captureVerticalMapSearch(); - await this._captureVerticalFullPageMapSearch(); - await this._captureDirectAnswers(); - } - - async _captureUniversalSearch () { - await this._pageNavigator.gotoUniversalPage(); - await this._camera.snapshot('universal-search'); - - await this._pageNavigator.gotoUniversalPage({ query: 'a' }); - await this._camera.snapshot('universal-search--no-results'); - - await this._pageNavigator.gotoUniversalPage({ query: 'office sparce'}); - await this._camera.snapshot('universal-search--spellcheck'); - - await this._pageNavigator.gotoUniversalPage({ query: 'what if i forget my password?'}); - await this._pageNavigator.click('.HitchhikerFaqAccordion-toggle') - await this._camera.snapshot('universal-search--faq-accordion'); - - await this._pageNavigator.gotoUniversalPage({ query: 'yext answers'}); - await this._camera.snapshot('universal-search--product-prominentimage'); - } - - async _captureVerticalSearch () { - await this._pageNavigator.gotoVerticalPage('events'); - await this._camera.snapshot('vertical-search'); - - await this._pageNavigator.gotoVerticalPage('events', { query: 'a' }); - await this._camera.snapshot('vertical-search--no-results'); - - await this._pageNavigator.gotoVerticalPage('events', { query: 'vrginia' }); - await this._camera.snapshot('vertical-search--spellcheck'); - - await this._pageNavigator.gotoVerticalPage('events_custom_cta_icons'); - await this._camera.snapshot('vertical-search--custom-cta-icons'); - - await this._pageNavigator.gotoVerticalPage('financial_professionals', { query: 'connor' }); - await this._camera.snapshot('vertical-search--financial-professional-location'); - - await this._pageNavigator.gotoVerticalPage('healthcare_professionals', { query: 'bob' }); - await this._camera.snapshot('vertical-search--healthcare-professional-location'); - - await this._pageNavigator.gotoVerticalPage('jobs', { query: 'job' }); - await this._camera.snapshot('vertical-search--job-standard'); - - await this._pageNavigator.gotoVerticalPage('help_articles', { query: 'slap chop' }); - await this._camera.snapshot('vertical-search--document-standard'); - - await this._pageNavigator.gotoVerticalPage('menu_items', { query: 'roll' }); - await this._camera.snapshot('vertical-search--menuitem-standard'); - } - - async _captureVerticalGridSearch () { - await this._pageNavigator.gotoVerticalPage('people', { query: 'a' }); - await this._camera.snapshot('vertical-grid-search'); - - await this._pageNavigator.gotoVerticalPage('people', { query: 'vrginia' }); - await this._camera.snapshot('vertical-grid-search--spellcheck'); - - await this._pageNavigator.gotoVerticalPage('products', { query: 'yext answers' }); - await this._camera.snapshot('vertical-grid-search--product-prominentvideo'); - - await this._pageNavigator.gotoVerticalPage('products_clickable_image', { query: 'yext answers' }); - await this._camera.snapshot('vertical-grid-search--product-prominentimage-clickable'); - } - - async _captureVerticalMapSearch () { - await this._pageNavigator.gotoVerticalPage('locations', { query: 'a' }); - await this._camera.snapshot('vertical-map-search'); - - await this._pageNavigator.gotoVerticalPage('locations_google', { query: 'virginia' }); - await this._camera.snapshot('vertical-map-search--google'); - } - - async _captureVerticalFullPageMapSearch () { - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: '' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map__desktop-view'); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-list-view'); - - await this._pageNavigator.click('.Answers-mobileToggle'); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-map-view'); - - const mapboxPinSelector = '.js-answersMap button'; - await this._pageNavigator.click(mapboxPinSelector); - await this._camera.snapshotMobileOnly('vertical-full-page-map__mobile-detail-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map_with_filters', { query: 'people' }); - await this._camera.snapshot('vertical-full-page-map--alternative-verticals'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'office sparce'}); - await this._camera.snapshotDesktopOnly('vertical-full-page-map--spellcheck__desktop-view'); - await this._camera.snapshotMobileOnly('vertical-full-page-map--spellcheck__mobile-list-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'virginia' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map--nlp-filters__desktop-view'); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map_with_filters', { query: 'virginia' }); - await this._camera.snapshotDesktopOnly('vertical-full-page-map-with-filters--nlp-filters__desktop-view'); - } - - async _captureDirectAnswers () { - await this._pageNavigator.gotoUniversalPage({ query: 'bryan reed phone number' }); - await this._camera.snapshot('field-direct-answer'); - - await this._pageNavigator.gotoUniversalPage({ query: 'where was joe exotic born?' }); - await this._camera.snapshot('documentsearch-direct-answer') - - await this._pageNavigator.gotoUniversalPage({ query: 'how to get rich text' }); - await this._camera.snapshot('documentsearch-rich-text-direct-answer') - - await this._pageNavigator.gotoUniversalPage({ query: 'who is howard?' }); - await this._camera.snapshot('documentsearch-rich-text-picture-direct-answer') - } -} - -module.exports = Photographer; diff --git a/tests/wcag/index.js b/tests/wcag/index.js index 16d4ea0cf..12be4986f 100755 --- a/tests/wcag/index.js +++ b/tests/wcag/index.js @@ -3,8 +3,9 @@ const HttpServer = require('../test-utils/server'); const { AxePuppeteer } = require('@axe-core/puppeteer'); const StandardPageNavigator = require('../percy/standardpagenavigator'); -const WcagReporter = require('./wcagreporter'); const puppeteer = require('puppeteer'); +const PageOperator = require('../browser-automation/pageoperator'); +const getTestingLocations = require('../browser-automation/testlocations'); const PORT = 5042; /** @@ -33,11 +34,15 @@ async function wcagTester() { const page = await browser.newPage(); const standardPageNavigator = new StandardPageNavigator(page, `http://localhost:${PORT}`); - const analyzer = await new AxePuppeteer(page).options(config); + const analyzer = new AxePuppeteer(page).options(config); let results = []; try { - results = await new WcagReporter(standardPageNavigator, analyzer, page).analyze(); + const operator = new PageOperator(standardPageNavigator, page, getTestingLocations()); + while (operator.hasNextTestLocation()) { + await operator.nextTestLocation(); + results.push(await analyzer.analyze()); + } } catch (e) { console.log(e); await browser.close(); diff --git a/tests/wcag/wcagreporter.js b/tests/wcag/wcagreporter.js deleted file mode 100644 index 44d7144b5..000000000 --- a/tests/wcag/wcagreporter.js +++ /dev/null @@ -1,147 +0,0 @@ -const { BrowserPageWidths } = require("../percy/constants"); - -class WcagReporter { - /** - * @param {PageNavigator} pageNavigator - * @param {import('@axe-core/puppeteer')} analyzer - * @param {import('puppeteer').Page} page - */ - constructor (pageNavigator, analyzer, page) { - this._pageNavigator = pageNavigator; - this._analyzer = analyzer; - this._page = page; - this.results = []; - } - - async analyze() { - await this._analyzeUniversalSearch(); - await this._analyzeVerticalSearch(); - await this._analyzeVerticalGridSearch(); - await this._analyzeVerticalMapSearch(); - await this._analyzeVerticalFullPageMapSearch(); - await this._analyzeDirectAnswers(); - return this.results; - } - - - async _analyzeUniversalSearch() { - await this._pageNavigator.gotoUniversalPage(); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'a' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'office sparce'}); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'what if i forget my password?'}); - await this._pageNavigator.click('.HitchhikerFaqAccordion-toggle') - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'yext answers'}); - this.results.push(await this._analyzer.analyze()); - - } - - async _analyzeVerticalSearch() { - await this._pageNavigator.gotoVerticalPage('events'); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('events', { query: 'a' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('events', { query: 'vrginia' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('events_custom_cta_icons'); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('financial_professionals', { query: 'connor' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('healthcare_professionals', { query: 'bob' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('jobs', { query: 'job' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('help_articles', { query: 'slap chop' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('menu_items', { query: 'roll' }); - this.results.push(await this._analyzer.analyze()); - } - - async _analyzeVerticalGridSearch () { - await this._pageNavigator.gotoVerticalPage('people', { query: 'a' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('people', { query: 'vrginia' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('products', { query: 'yext answers' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('products_clickable_image', { query: 'yext answers' }); - this.results.push(await this._analyzer.analyze()); - } - - async _analyzeVerticalMapSearch () { - await this._pageNavigator.gotoVerticalPage('locations', { query: 'a' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoVerticalPage('locations_google', { query: 'virginia' }); - this.results.push(await this._analyzer.analyze()); - } - - async _analyzeVerticalFullPageMapSearch () { - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: '' }); - this.results.push(await this._analyzer.analyze()); - - await this._page.setViewport({ width: BrowserPageWidths.Mobile, height: this._page.viewport().height }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.click('.Answers-mobileToggle'); - this.results.push(await this._analyzer.analyze()); - - const mapboxPinSelector = '.js-answersMap button'; - await this._pageNavigator.click(mapboxPinSelector); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'office sparce'}); - this.results.push(await this._analyzer.analyze()); - - await this._page.setViewport({ width: BrowserPageWidths.Desktop, height: this._page.viewport().height }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map', { query: 'virginia' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map_with_filters', { query: 'people' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator - .gotoVerticalPage('locations_full_page_map_with_filters', { query: 'virginia' }); - this.results.push(await this._analyzer.analyze()); - } - - async _analyzeDirectAnswers () { - await this._pageNavigator.gotoUniversalPage({ query: 'bryan reed phone number' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'where was joe exotic born?' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'how to get rich text' }); - this.results.push(await this._analyzer.analyze()); - - await this._pageNavigator.gotoUniversalPage({ query: 'who is howard?' }); - this.results.push(await this._analyzer.analyze()); - } -} - -module.exports = WcagReporter; From 38bb48a9403dc124fa94e91f34e01551e68030ab Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Thu, 18 Nov 2021 11:26:17 -0500 Subject: [PATCH 07/19] Update default universal limit (#1010) Change the default universalLimit for all vertical page-configs to 4. J=SLAP-1681 TEST=manual Smoke test in test-site and see that faq universal results are limited to 4 when uncommented. --- templates/vertical-full-page-map/page-config.json | 2 +- templates/vertical-grid/page-config.json | 2 +- templates/vertical-map/page-config.json | 2 +- templates/vertical-standard/page-config.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/vertical-full-page-map/page-config.json b/templates/vertical-full-page-map/page-config.json index 3d5142d4a..a7ab84af4 100644 --- a/templates/vertical-full-page-map/page-config.json +++ b/templates/vertical-full-page-map/page-config.json @@ -64,7 +64,7 @@ "": { // The vertical key from your search configuration // "label": "", // The name of the vertical in the section header and the navigation bar // "verticalLimit": 15, // The result count limit for vertical search - // "universalLimit": 5, // The result count limit for universal search + "universalLimit": 4, // The result count limit for universal search "cardType": "location-standard", // The name of the card to use - e.g. accordion, location, customcard // "icon": "pin", // The icon to use on the card for this vertical "mapConfig": { diff --git a/templates/vertical-grid/page-config.json b/templates/vertical-grid/page-config.json index 7d1c4279d..84cc96508 100644 --- a/templates/vertical-grid/page-config.json +++ b/templates/vertical-grid/page-config.json @@ -66,7 +66,7 @@ "": { // The vertical key from your search configuration // "label": "", // The name of the vertical in the section header and the navigation bar // "verticalLimit": 15, // The result count limit for vertical search - // "universalLimit": 5, // The result count limit for universal search + "universalLimit": 4, // The result count limit for universal search "cardType": "product-prominentimage", // The name of the card to use - e.g. accordion, location, customcard // "icon": "star", // The icon to use on the card for this vertical "universalSectionTemplate": "standard" diff --git a/templates/vertical-map/page-config.json b/templates/vertical-map/page-config.json index 21beb8b33..dd5752c7a 100644 --- a/templates/vertical-map/page-config.json +++ b/templates/vertical-map/page-config.json @@ -66,7 +66,7 @@ "": { // The vertical key from your search configuration // "label": "", // The name of the vertical in the section header and the navigation bar // "verticalLimit": 15, // The result count limit for vertical search - // "universalLimit": 5, // The result count limit for universal search + "universalLimit": 4, // The result count limit for universal search "cardType": "location-standard", // The name of the card to use - e.g. accordion, location, customcard // "icon": "pin", // The icon to use on the card for this vertical "mapConfig": { diff --git a/templates/vertical-standard/page-config.json b/templates/vertical-standard/page-config.json index b2d9a4bac..3ae907bc4 100644 --- a/templates/vertical-standard/page-config.json +++ b/templates/vertical-standard/page-config.json @@ -78,7 +78,7 @@ "": { // The vertical key from your search configuration // "label": "", // The name of the vertical in the section header and the navigation bar // "verticalLimit": 15, // The result count limit for vertical search - // "universalLimit": 5, // The result count limit for universal search + "universalLimit": 4, // The result count limit for universal search "cardType": "standard", // The name of the card to use - e.g. accordion, location, customcard // "icon": "star", // The icon to use on the card for this vertical "universalSectionTemplate": "standard" From 1322a6eac4e2f364610216bb994b974fb79c5a02 Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Mon, 22 Nov 2021 12:36:18 -0500 Subject: [PATCH 08/19] move spellcheck above results count on vertical templates (#1011) J=SLAP-1678 TEST=manual built vertical-grid and vertical-standard page in english checked spellcheck looks as expected for mispelled searches --- templates/vertical-grid/page.html.hbs | 2 +- templates/vertical-standard/page.html.hbs | 2 +- test-site/pages-patches/people.html.hbs.patch | 13 +------------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/templates/vertical-grid/page.html.hbs b/templates/vertical-grid/page.html.hbs index 69ad32d03..1362ae503 100644 --- a/templates/vertical-grid/page.html.hbs +++ b/templates/vertical-grid/page.html.hbs @@ -27,6 +27,7 @@ partial for this page, you should not use it on any other page. --> {{!-- {{> layouts/overlay-suggestions }} --}}
+ {{> templates/vertical-grid/markup/spellcheck }}
{{> templates/vertical-grid/markup/verticalresultscount }} {{> templates/vertical-grid/markup/appliedfilters }} @@ -40,7 +41,6 @@ {{!-- {{> templates/vertical-grid/markup/facets }} --}} {{!--
--}}
- {{> templates/vertical-grid/markup/spellcheck }} {{> templates/vertical-grid/markup/verticalresults }} {{> templates/vertical-grid/markup/pagination }} {{!-- {{> templates/vertical-grid/markup/qasubmission }} --}} diff --git a/templates/vertical-standard/page.html.hbs b/templates/vertical-standard/page.html.hbs index a018e1879..767d24184 100644 --- a/templates/vertical-standard/page.html.hbs +++ b/templates/vertical-standard/page.html.hbs @@ -30,6 +30,7 @@ {{!-- {{> layouts/overlay-suggestions }} --}}
{{!-- {{> templates/vertical-standard/markup/directanswer }} --}} + {{> templates/vertical-standard/markup/spellcheck }}
{{> templates/vertical-standard/markup/verticalresultscount }} {{> templates/vertical-standard/markup/appliedfilters }} @@ -43,7 +44,6 @@ {{!-- {{> templates/vertical-standard/markup/facets }} --}} {{!--
--}}
- {{> templates/vertical-standard/markup/spellcheck }} {{> templates/vertical-standard/markup/verticalresults }} {{> templates/vertical-standard/markup/pagination }} {{!-- {{> templates/vertical-standard/markup/qasubmission }} --}} diff --git a/test-site/pages-patches/people.html.hbs.patch b/test-site/pages-patches/people.html.hbs.patch index 1f0092700..2b0771874 100644 --- a/test-site/pages-patches/people.html.hbs.patch +++ b/test-site/pages-patches/people.html.hbs.patch @@ -20,18 +20,7 @@ {{!-- {{> templates/vertical-grid/script/filterbox }} --}} {{!-- {{> templates/vertical-grid/script/qasubmission }} --}} {{/script/core }} -@@ -22,23 +22,23 @@ - {{> templates/vertical-grid/markup/navigation }} -
-
-- -+ -+ - {{!-- {{> layouts/overlay-suggestions }} --}} -
+@@ -30,15 +30,15 @@
{{> templates/vertical-grid/markup/verticalresultscount }} {{> templates/vertical-grid/markup/appliedfilters }} From ab4dfe8dd6bb881c19c673041e641a7f6ac71327 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Mon, 29 Nov 2021 13:54:46 -0500 Subject: [PATCH 09/19] Escape angle brackets in highlightField (#1012) - perform full HTML escape on the constructed highlighted text (will transform ", ', &, <, and >) - `substr` is deprecated, replaced with `substring` J=SLAP-1703 TEST=manual & auto update bryan's entity description to include script tags, search for 'where is joe exotic' and see that the script tags appear. (without the replacement, they do not appear) see added jest tests passed --- package-lock.json | 9 +++-- package.json | 3 ++ static/js/formatters-internal.js | 17 ++++---- .../js/formatters-internal/highlightField.js | 39 +++++++++++++++++++ 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 tests/static/js/formatters-internal/highlightField.js diff --git a/package-lock.json b/package-lock.json index cb22165f5..cb6245f15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "answers-hitchhiker-theme", "version": "1.26.0", + "dependencies": { + "escape-html": "^1.0.3" + }, "devDependencies": { "@axe-core/puppeteer": "^4.3.1", "@babel/core": "^7.9.6", @@ -7509,8 +7512,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "node_modules/escape-string-regexp": { "version": "1.0.5", @@ -23390,8 +23392,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", diff --git a/package.json b/package.json index bd2251e7c..33a88f8b4 100644 --- a/package.json +++ b/package.json @@ -80,5 +80,8 @@ "/wcag/", "/acceptance/" ] + }, + "dependencies": { + "escape-html": "^1.0.3" } } diff --git a/static/js/formatters-internal.js b/static/js/formatters-internal.js index 5e4137afe..113e82b79 100644 --- a/static/js/formatters-internal.js +++ b/static/js/formatters-internal.js @@ -11,6 +11,7 @@ import { isChrome } from './useragent.js'; import LocaleCurrency from 'locale-currency' import getSymbolFromCurrency from 'currency-symbol-map' import { parseLocale } from './utils.js'; +import escape from 'escape-html'; export function address(profile) { if (!profile.address) { @@ -540,20 +541,20 @@ export function priceRange(defaultPriceRange, countryCode) { * highlight. */ export function highlightField(fieldValue, matchedSubstrings = []) { - let highlightedString = fieldValue; + let highlightedString = ''; - // We must first sort the matchedSubstrings by decreasing offset. + // We must first sort the matchedSubstrings by ascending offset. const sortedMatches = matchedSubstrings.slice() - .sort((match1, match2) => match2.offset - match1.offset); + .sort((match1, match2) => match1.offset - match2.offset); + let processedFieldValueIndex = 0; sortedMatches.forEach(match => { const { offset, length } = match; - highlightedString = - highlightedString.substr(0, offset) + - `${fieldValue.substr(offset, length)}`+ - highlightedString.substr(offset + length); + highlightedString += escape(fieldValue.substring(processedFieldValueIndex, offset)) + + `${escape(fieldValue.substring(offset, offset + length))}`; + processedFieldValueIndex = offset + length; }); - + highlightedString += escape(fieldValue.substring(processedFieldValueIndex)); return highlightedString; } diff --git a/tests/static/js/formatters-internal/highlightField.js b/tests/static/js/formatters-internal/highlightField.js new file mode 100644 index 000000000..2d7390808 --- /dev/null +++ b/tests/static/js/formatters-internal/highlightField.js @@ -0,0 +1,39 @@ +import Formatters from '../../../../static/js/formatters'; +const { highlightField } = Formatters; + +describe('highlightField properly handle snippets', () => { + it('snippet with empty matched substrings', () => { + const fieldValue = 'morbi tristique senectus et netus et malesuada fames ac turpis egestas.'; + const matchedSubstrings = []; + expect(highlightField(fieldValue, matchedSubstrings)).toEqual(fieldValue); + }); + + it('snippet with matched substrings at beginning of field value', () => { + const fieldValue = 'morbi tristique senectus et netus et malesuada fames ac turpis egestas.'; + const matchedSubstrings = [{ offset: 0, length: 5 }]; + + const expectedString = 'morbi tristique senectus et netus et malesuada fames ac turpis egestas.'; + expect(highlightField(fieldValue, matchedSubstrings)).toEqual(expectedString); + }); + + it('snippet with matched substrings at end of field value', () => { + const fieldValue = 'morbi tristique senectus et netus et malesuada fames ac turpis egestas.'; + const matchedSubstrings = [{ offset: 56, length: 15 }]; + + const expectedString = 'morbi tristique senectus et netus et malesuada fames ac turpis egestas.'; + expect(highlightField(fieldValue, matchedSubstrings)).toEqual(expectedString); + }); + + it('handle snippet with non-html brackets and multiple matched substrings', () => { + const fieldValue = '... script> some stuff 2 Joe Exotic was born Joseph Allen Schreibvogel in Garden City, ' + + 'Kansas, on March 5, 1963.[6][7][8], to parents Francis and Shirley Schreibvogel.[9] He grew up on a working farm ' + + 'in Kansas. He was Joseph Allen Schreibvogel in Garden City ...'; + const matchedSubstrings = [{ offset: 144, length: 32 }, { offset: 84, length: 19 }]; + + const expectedString = '... script> some stuff 2</script> Joe Exotic was born Joseph Allen Schreibvogel in' + + ' Garden City, Kansas, on March 5, 1963.[6][7][8], to parents Francis and Shirley Schreibvogel' + + '.[9] He grew up on a working farm in Kansas. <script> some ampersand stuff & & </script>' + + ' He was Joseph Allen Schreibvogel in Garden City ...'; + expect(highlightField(fieldValue, matchedSubstrings)).toEqual(expectedString); + }); +}); \ No newline at end of file From 14be64fe97936e37fda14c388dd6b7df7edb24eb Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Mon, 29 Nov 2021 15:38:36 -0500 Subject: [PATCH 10/19] Automate version update (#1013) - Add github action to create a pr with version update commit to a release/hotfix branch on create. J=SLAP-1356 --- .github/workflows/version-update.yml | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/version-update.yml diff --git a/.github/workflows/version-update.yml b/.github/workflows/version-update.yml new file mode 100644 index 000000000..4111d8d7f --- /dev/null +++ b/.github/workflows/version-update.yml @@ -0,0 +1,44 @@ +name: Update package version for release & hotfix branches + +on: + create: + branches: [release/*, hotfix/*] + +permissions: + contents: write + pull-requests: write + +jobs: + update-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + - name: update package version + id: vars + run: | + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + PACKAGE_VERSION="${GITHUB_REF##*/}" + echo ::set-output name=branch::${BRANCH_NAME} + if [[ $PACKAGE_VERSION =~ ^v[0-9]+\.[0-9]+(\.[0-9]+)?$ ]] + then + if [[ $PACKAGE_VERSION =~ ^v[0-9]+\.[0-9]+$ ]] + then + PACKAGE_VERSION="${PACKAGE_VERSION}.0" + fi + echo ::set-output name=version::${PACKAGE_VERSION} + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + npm version ${PACKAGE_VERSION} + git push -u origin HEAD:"dev/update-version-${PACKAGE_VERSION}" + else + echo "Branch name ${BRANCH_NAME} does not have the correct format with package version." + exit 1 + fi + - uses: repo-sync/pull-request@v2 + with: + source_branch: "dev/update-version-${{ steps.vars.outputs.version }}" + destination_branch: "${{ steps.vars.outputs.branch }}" + pr_title: "Update Package Version to ${{ steps.vars.outputs.version }}" + pr_body: "*An automated PR which updates the version number in package.json and package-lock.json files*" + github_token: ${{ secrets.GITHUB_TOKEN }} From 2b71bfdc3e4caee43e769f19d82b0cdd35d02e16 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Mon, 29 Nov 2021 17:47:59 -0500 Subject: [PATCH 11/19] Update escape packages (#1014) - update escape-html to be devDependency in top level package - add escape-html as depeendency in static package --- package-lock.json | 10 +++++----- package.json | 4 +--- static/package-lock.json | 31 +++++++++++++++++++++++++++++++ static/package.json | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb6245f15..6ede5a7f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,6 @@ "": { "name": "answers-hitchhiker-theme", "version": "1.26.0", - "dependencies": { - "escape-html": "^1.0.3" - }, "devDependencies": { "@axe-core/puppeteer": "^4.3.1", "@babel/core": "^7.9.6", @@ -22,6 +19,7 @@ "comment-json": "^4.1.1", "cross-env": "^7.0.2", "currency-symbol-map": "^5.0.1", + "escape-html": "^1.0.3", "express": "^4.17.1", "file-system": "^2.2.2", "full-icu": "^1.3.1", @@ -7512,7 +7510,8 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true }, "node_modules/escape-string-regexp": { "version": "1.0.5", @@ -23392,7 +23391,8 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true }, "escape-string-regexp": { "version": "1.0.5", diff --git a/package.json b/package.json index 33a88f8b4..dc4806a5b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "comment-json": "^4.1.1", "cross-env": "^7.0.2", "currency-symbol-map": "^5.0.1", + "escape-html": "^1.0.3", "express": "^4.17.1", "file-system": "^2.2.2", "full-icu": "^1.3.1", @@ -80,8 +81,5 @@ "/wcag/", "/acceptance/" ] - }, - "dependencies": { - "escape-html": "^1.0.3" } } diff --git a/static/package-lock.json b/static/package-lock.json index 5ba5f8a57..4bef4614c 100644 --- a/static/package-lock.json +++ b/static/package-lock.json @@ -2877,6 +2877,11 @@ "integrity": "sha512-DR6NO3h9niOT+MZs7bjxlj2a1k+POu5RN8CLTPX2+i78bRi9eLe7+0zXgUHMnGXWybYcL61E9hGhPKqedy8tQA==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -4298,6 +4303,26 @@ "ms": "2.1.2" } }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "simple-git": { "version": "2.46.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.46.0.tgz", @@ -4308,6 +4333,12 @@ "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.3.1" } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true } } }, diff --git a/static/package.json b/static/package.json index 456946b07..1763e3d9d 100644 --- a/static/package.json +++ b/static/package.json @@ -49,6 +49,7 @@ "@vimeo/player": "^2.15.3", "core-js": "^3.6.5", "css-vars-ponyfill": "^2.3.1", + "escape-html": "^1.0.3", "file-system": "^2.2.2", "fs-extra": "^8.1.0", "iframe-resizer": "^4.1.1", From f1fad30a2a2320546ee89f422c2eb0dd85e1ac02 Mon Sep 17 00:00:00 2001 From: nmanu1 <88398086+nmanu1@users.noreply.github.com> Date: Fri, 3 Dec 2021 08:44:46 -0500 Subject: [PATCH 12/19] Remove scrollbars from filters (#1015) Change styling of `yxt-FilterOptions-options` to remove scrollbars that were appearing on filters even when all options were displayed for some cases with custom font sizes. Add a `--yxt-filters-and-sorts-line-height` variable so that customers can change the line-height of the `optionLabel` easily if the scrollbar appears with even larger custom font sizes. J=SLAP-1727 TEST=visual See that filter scrollbar was removed on a site where it was previously appearing. --- static/scss/answers-variables.scss | 1 + static/scss/answers/answers-variables-default.scss | 1 + .../answers/collapsible-filters/sdk-filter-overrides.scss | 5 +++-- static/scss/answers/theme.scss | 4 ++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/static/scss/answers-variables.scss b/static/scss/answers-variables.scss index ff509225d..acef5b9af 100644 --- a/static/scss/answers-variables.scss +++ b/static/scss/answers-variables.scss @@ -51,6 +51,7 @@ $container-width: 1000px; --yxt-searchbar-button-text-color-hover: var(--yxt-color-brand-primary); --yxt-filter-options-options-max-height: none; --yxt-filters-and-sorts-font-size: var(--yxt-font-size-md); + --yxt-filters-option-label-line-height: 20px; --yxt-alternative-verticals-emphasized-font-weight: var(--yxt-font-weight-semibold); --yxt-text-snippet-highlight-color: #eef3fb; --yxt-text-snippet-font-color: var(--yxt-color-text-primary); diff --git a/static/scss/answers/answers-variables-default.scss b/static/scss/answers/answers-variables-default.scss index 7dfefb3d6..7eba4c66d 100644 --- a/static/scss/answers/answers-variables-default.scss +++ b/static/scss/answers/answers-variables-default.scss @@ -50,6 +50,7 @@ $container-width: 1000px; --yxt-searchbar-button-text-color-hover: var(--yxt-color-brand-primary); --yxt-filter-options-options-max-height: none; --yxt-filters-and-sorts-font-size: var(--yxt-font-size-md); + --yxt-filters-option-label-line-height: 20px; --yxt-alternative-verticals-emphasized-font-weight: var(--yxt-font-weight-semibold); --yxt-text-snippet-highlight-color: #eef3fb; --yxt-text-snippet-font-color: var(--yxt-color-text-primary); diff --git a/static/scss/answers/collapsible-filters/sdk-filter-overrides.scss b/static/scss/answers/collapsible-filters/sdk-filter-overrides.scss index 75f74d44d..44a9765cd 100644 --- a/static/scss/answers/collapsible-filters/sdk-filter-overrides.scss +++ b/static/scss/answers/collapsible-filters/sdk-filter-overrides.scss @@ -23,8 +23,9 @@ } &-options { - margin-top: 4px; - margin-bottom: 4px; + margin: 0; + padding-top: 4px; + padding-bottom: 4px; padding-left: 4px; } diff --git a/static/scss/answers/theme.scss b/static/scss/answers/theme.scss index 30b59e43d..866a90e3a 100644 --- a/static/scss/answers/theme.scss +++ b/static/scss/answers/theme.scss @@ -194,6 +194,10 @@ font-size: var(--yxt-filters-and-sorts-font-size); } + .yxt-FilterOptions-optionLabel { + line-height: var(--yxt-filters-option-label-line-height); + } + .yxt-Card-child { mark { background-color: unset; From 8fbc7296811b01de4b135e3e13c75ae6198e8d85 Mon Sep 17 00:00:00 2001 From: Yen Truong <36055303+yen-tt@users.noreply.github.com> Date: Fri, 3 Dec 2021 09:57:12 -0500 Subject: [PATCH 13/19] fix (#1016) - The create event does not support branch filter and tag filter so this would run on any branch creation. Update to trigger on push from release or hotfix branch. Then check if version is updated before making a new branch for pr J=none TEST=manual test in dummy repo, see version update on hotfix/release branch, but not in dev branches --- .github/workflows/version-update.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/version-update.yml b/.github/workflows/version-update.yml index 4111d8d7f..e4a944be6 100644 --- a/.github/workflows/version-update.yml +++ b/.github/workflows/version-update.yml @@ -1,7 +1,7 @@ name: Update package version for release & hotfix branches on: - create: + push: branches: [release/*, hotfix/*] permissions: @@ -29,13 +29,21 @@ jobs: echo ::set-output name=version::${PACKAGE_VERSION} git config user.name 'github-actions[bot]' git config user.email 'github-actions[bot]@users.noreply.github.com' - npm version ${PACKAGE_VERSION} + if npm version ${PACKAGE_VERSION} | tee >( grep -q 'npm ERR! Version not changed' ) + then + echo "Package version is already in sync with branch name." + echo ::set-output name=should_create_pr::0 + exit 0 + fi + echo ::set-output name=should_create_pr::1 git push -u origin HEAD:"dev/update-version-${PACKAGE_VERSION}" else echo "Branch name ${BRANCH_NAME} does not have the correct format with package version." exit 1 fi - - uses: repo-sync/pull-request@v2 + - name: create version update pr + if: steps.vars.outputs.should_create_pr == 1 + uses: repo-sync/pull-request@v2 with: source_branch: "dev/update-version-${{ steps.vars.outputs.version }}" destination_branch: "${{ steps.vars.outputs.branch }}" From 4ff3f0d043bdaa0853703172c3a89d30532879dc Mon Sep 17 00:00:00 2001 From: Oliver Shi Date: Fri, 10 Dec 2021 10:45:18 -0500 Subject: [PATCH 14/19] move spellcheck above results count in vertical-map template (#1017) J=SLAP-1678 TEST=manual rebuilt test site, looks as expected --- templates/vertical-map/page.html.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/vertical-map/page.html.hbs b/templates/vertical-map/page.html.hbs index 39991564b..e9e21d377 100644 --- a/templates/vertical-map/page.html.hbs +++ b/templates/vertical-map/page.html.hbs @@ -29,6 +29,7 @@ {{!-- {{> layouts/overlay-suggestions }} --}}
+ {{> templates/vertical-map/markup/spellcheck }}
{{> templates/vertical-map/markup/verticalresultscount }} {{> templates/vertical-map/markup/appliedfilters }} @@ -42,7 +43,6 @@ {{!-- {{> templates/vertical-map/markup/filterbox }} --}} {{!--
--}}
- {{> templates/vertical-map/markup/spellcheck }} {{> templates/vertical-map/markup/verticalresults }} {{> templates/vertical-map/markup/pagination }} {{!-- {{> templates/vertical-map/markup/qasubmission }} --}} From 20e70af9bcaf2b7e8486d8d37b5023a74b3e875e Mon Sep 17 00:00:00 2001 From: Tom Meyer Date: Mon, 13 Dec 2021 10:21:59 -0500 Subject: [PATCH 15/19] Update the name of the CSS variable controlling option label `line-height`. (#1019) The variable name was changed from `--yxt-filters-option-label-line-height` to `--yxt-filter-options-option-label-line-height`. TEST=none Simply did a find/replace all. --- static/scss/answers-variables.scss | 2 +- static/scss/answers/answers-variables-default.scss | 2 +- static/scss/answers/theme.scss | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/static/scss/answers-variables.scss b/static/scss/answers-variables.scss index acef5b9af..380b018c4 100644 --- a/static/scss/answers-variables.scss +++ b/static/scss/answers-variables.scss @@ -51,7 +51,7 @@ $container-width: 1000px; --yxt-searchbar-button-text-color-hover: var(--yxt-color-brand-primary); --yxt-filter-options-options-max-height: none; --yxt-filters-and-sorts-font-size: var(--yxt-font-size-md); - --yxt-filters-option-label-line-height: 20px; + --yxt-filter-options-option-label-line-height: 20px; --yxt-alternative-verticals-emphasized-font-weight: var(--yxt-font-weight-semibold); --yxt-text-snippet-highlight-color: #eef3fb; --yxt-text-snippet-font-color: var(--yxt-color-text-primary); diff --git a/static/scss/answers/answers-variables-default.scss b/static/scss/answers/answers-variables-default.scss index 7eba4c66d..24e487bfd 100644 --- a/static/scss/answers/answers-variables-default.scss +++ b/static/scss/answers/answers-variables-default.scss @@ -50,7 +50,7 @@ $container-width: 1000px; --yxt-searchbar-button-text-color-hover: var(--yxt-color-brand-primary); --yxt-filter-options-options-max-height: none; --yxt-filters-and-sorts-font-size: var(--yxt-font-size-md); - --yxt-filters-option-label-line-height: 20px; + --yxt-filter-options-option-label-line-height: 20px; --yxt-alternative-verticals-emphasized-font-weight: var(--yxt-font-weight-semibold); --yxt-text-snippet-highlight-color: #eef3fb; --yxt-text-snippet-font-color: var(--yxt-color-text-primary); diff --git a/static/scss/answers/theme.scss b/static/scss/answers/theme.scss index 866a90e3a..41ecbe3f7 100644 --- a/static/scss/answers/theme.scss +++ b/static/scss/answers/theme.scss @@ -195,7 +195,7 @@ } .yxt-FilterOptions-optionLabel { - line-height: var(--yxt-filters-option-label-line-height); + line-height: var(--yxt-filter-options-option-label-line-height); } .yxt-Card-child { From 2f8e36324c3fb254b15043bc83a314b9321317bc Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Mon, 13 Dec 2021 11:59:02 -0500 Subject: [PATCH 16/19] Fix preloads on multilang sites (#1018) Fix font preloads on multilang sites and no longer generate a hash for fonts When webpack was processing the preload tags to update them to use the hash, it was dropping the relative path which jambo was adding which caused the preloads to point to invalid links on multilang sites. I couldn't find an option to adjust the path names after webpack processed it, so the simpler solution is to remove the hashes from the fonts. That way webpack will no longer need to update the font preloads and we can instead rely on the relative path provided by Jambo. There was an additional bug where we had an extra dot in the font names. (e.g. source-sans..woff), and this change fixes that as well. Because we are removing the hash, that makes it more difficult to cache bust it. If someone uses a new font file and names it exactly the same as one of the previous fonts, they cache will return the old asset. The current font names are so specific that I find this unlikely to be a problem (e.g. source-sans-pro-v14-latin-300.woff). J=SLAP-1724 TEST=manual Check the network tab for font loads on english experiences and on multilang pages. Check both universal and vertical pages and observe that the error in the console about invalid font preloads is gone for multilang sites. See that the loaded font no longer has an extra dot before the extension. --- layouts/preload-fonts.hbs | 8 ++++---- static/webpack-config.js | 33 +-------------------------------- 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/layouts/preload-fonts.hbs b/layouts/preload-fonts.hbs index 8c573ab01..63c87df0a 100644 --- a/layouts/preload-fonts.hbs +++ b/layouts/preload-fonts.hbs @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/static/webpack-config.js b/static/webpack-config.js index a1e3fcce8..6fae95617 100644 --- a/static/webpack-config.js +++ b/static/webpack-config.js @@ -143,42 +143,11 @@ module.exports = function () { } ], }, - { - test: /\.html$/i, - use: [ - { - loader: 'html-loader', - options: { - sources: { - list: [ - { - tag: 'link', - attribute: 'href', - type: 'src', - filter: (tag, attribute, attributes) => { - const isPreload = attributes.find(attr => { - return attr.name === 'rel' && attr.value === 'preload'; - }); - const isFont = attributes.find(attr => { - return attr.name === 'as' && attr.value === 'font'; - }); - return isPreload && isFont; - } - } - ], - urlFilter: (attribute, value) => { - return /^[./]*static\/assets\//.test(value); - } - }, - } - } - ] - }, { test: /\.(png|ico|gif|jpe?g|svg|webp|eot|otf|ttf|woff2?)$/, type: 'asset/resource', generator: { - filename: '[name].[hash].[ext]' + filename: '[name][ext]' } } ], From 6b0e5501c7d23c7ce2d6db2a884027dd94530b21 Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Mon, 13 Dec 2021 14:04:30 -0500 Subject: [PATCH 17/19] Bump version to v1.27.0 (#1020) Bump version to v1.27.0 J=none TEST=none --- package-lock.json | 2 +- package.json | 2 +- static/package-lock.json | 2 +- static/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ede5a7f1..bbe4e385a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "answers-hitchhiker-theme", - "version": "1.26.0", + "version": "1.27.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index dc4806a5b..ccb0e43f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "answers-hitchhiker-theme", - "version": "1.26.0", + "version": "1.27.0", "description": "A starter answers theme for hitchhikers", "scripts": { "test": "cross-env NODE_ICU_DATA=node_modules/full-icu jest --verbose", diff --git a/static/package-lock.json b/static/package-lock.json index 4bef4614c..a14deae29 100644 --- a/static/package-lock.json +++ b/static/package-lock.json @@ -1,6 +1,6 @@ { "name": "answers-hitchhiker-theme", - "version": "1.26.0", + "version": "1.27.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/static/package.json b/static/package.json index 1763e3d9d..33077004e 100644 --- a/static/package.json +++ b/static/package.json @@ -1,6 +1,6 @@ { "name": "answers-hitchhiker-theme", - "version": "1.26.0", + "version": "1.27.0", "description": "Toolchain for use with the HH Theme", "main": "Gruntfile.js", "scripts": { From e958b681be5291d1cfa7d807ac91e6cc5febaf28 Mon Sep 17 00:00:00 2001 From: Tom Meyer Date: Mon, 13 Dec 2021 17:17:28 -0500 Subject: [PATCH 18/19] Fix handling of `universalLimit` in `verticalsToConfig`. (#1021) Previously, if a `universalLimit` were specified for a vertical, we would perform client-side filtering. Instead, we should be creating a `search.universalLimit` object and passing that to `ANSWERS.init`. Not only does this remove responsibility from the front-end, but it allows the HH to display more than 10 results per vertical on the Universal page. This PR adds a `getDefaultUniversalLimit` helper that iterates through the vertical page configs and uses the `verticalsToConfig.universalLimits` to build a `search.universalLimit`. J=SLAP-1749 TEST=manual Tested the following scenarios and verified correct behavior in each: - No `universalLimit` values are set and there is no limit specified in the `global_config` or `pageSettings`. - Some `universalLimit` values are set, but not others. There is no universal limit in the `global_config` or `pageSettings`. - Some `universalLimit` values are set, but a universal limit is also present in the `global_config`. --- hbshelpers/getDefaultUniversalLimit.js | 27 +++++++++++++++++++ script/core.hbs | 6 +++++ .../script/universalresults.hbs | 13 --------- 3 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 hbshelpers/getDefaultUniversalLimit.js diff --git a/hbshelpers/getDefaultUniversalLimit.js b/hbshelpers/getDefaultUniversalLimit.js new file mode 100644 index 000000000..b63f0abe6 --- /dev/null +++ b/hbshelpers/getDefaultUniversalLimit.js @@ -0,0 +1,27 @@ +/** + * Generates a default result limit for each vertical on the universal page. The + * default is taken from the vertical's `universalLimit`, specified in the related + * page's VTC. If there is no such `universalLimit`, the back-end default of 10 will + * be used. + * + * @param {Object} pageConfigs - The configurations for each page. + * @returns {Object} The partial of the search configuration related to universal limits. + */ + module.exports = function getDefaultUniversalLimit(pageConfigs) { + const universalLimit = Object.entries(pageConfigs) + .filter(([pageName, _]) => pageName != 'index') + .reduce((limit, [_, pageConfig]) => { + const verticalKey = pageConfig.verticalKey; + const hasUniversalLimit = + pageConfig.verticalsToConfig && + pageConfig.verticalsToConfig[verticalKey] && + pageConfig.verticalsToConfig[verticalKey].universalLimit; + if (hasUniversalLimit) { + limit[verticalKey] = pageConfig.verticalsToConfig[verticalKey].universalLimit; + } + + return limit; + }, {}); + + return { universalLimit }; +} \ No newline at end of file diff --git a/script/core.hbs b/script/core.hbs index ab138bfa4..03cbeac98 100644 --- a/script/core.hbs +++ b/script/core.hbs @@ -28,6 +28,12 @@ ...{{{ json search }}} }, {{/if}} + {{#unless ../verticalKey}} + search: { + ...{{{ json (getDefaultUniversalLimit ../verticalConfigs) }}}, + ...{{{ json search }}} + }, + {{/unless}} {{/with}} }; const token = window.AnswersExperience.runtimeConfig?.get('token'); diff --git a/templates/universal-standard/script/universalresults.hbs b/templates/universal-standard/script/universalresults.hbs index 6dab2c0d7..891dd7cd3 100644 --- a/templates/universal-standard/script/universalresults.hbs +++ b/templates/universal-standard/script/universalresults.hbs @@ -88,19 +88,6 @@ ANSWERS.addComponent("UniversalResults", Object.assign({}, { pin: {{> templates/universal-standard/script/map-pin mapConfig }}, }), {{/if}} - {{#if universalLimit}} - transformData: (data) => { - let results = data.results; - if (results) { - results = results.filter((rex, idx) => { - return idx < {{{universalLimit}}}; - }); - } - return Object.assign(data, { - results: results - }); - }, - {{/if}} {{/inline}} {{!-- From c12bb5177e5095929963a82b37f98c688a322c6d Mon Sep 17 00:00:00 2001 From: cea2aj <42848445+cea2aj@users.noreply.github.com> Date: Tue, 14 Dec 2021 12:50:18 -0500 Subject: [PATCH 19/19] Testcafe Fixes (#1024) Fix Testcafe errors in safari by bumping the Testcafe version number This PR fixes the "undefined is not an object (evaluating 'qa.objectHasOwnProperty.call(s,"writable")') hasOwnProperty@[native code]" error which was occurring in Testcafe on Safari. Also fix the path to the testcafe config file. J=none TEST=none Run the acceptance tests and see that they now pass --- package-lock.json | 306 +++++++++++++++++++------------------- package.json | 2 +- tests/acceptance/index.js | 2 +- 3 files changed, 152 insertions(+), 158 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbe4e385a..96549a107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "answers-hitchhiker-theme", - "version": "1.26.0", + "version": "1.27.0", "devDependencies": { "@axe-core/puppeteer": "^4.3.1", "@babel/core": "^7.9.6", @@ -34,7 +34,7 @@ "puppeteer": "^10.2.0", "serve": "^11.3.2", "simple-git": "^2.24.0", - "testcafe": "^1.15.0", + "testcafe": "^1.17.1", "testcafe-browser-provider-browserstack": "^1.13.1", "underscore.string": "^3.3.5", "urijs": "1.18.12", @@ -3474,6 +3474,15 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "dev": true }, + "node_modules/@miherlosev/esm": { + "version": "3.2.26", + "resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz", + "integrity": "sha512-TaW4jTGVE1/ln2VGFChnheMh589QCAZy1MVnLvjjSzZ4pEAa4WYAWPwFkDVZbSdPQdLfZy7LuTyZjWRkhX9/Gg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -4691,9 +4700,9 @@ } }, "node_modules/acorn-hammerhead": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.4.0.tgz", - "integrity": "sha512-zMjPa6kNgMB0zclCZI41sPofSeeHMF9Q6e3ALRsowmmNqoiz1qiJI9oemt9GfZ5e5EmQpElvePT3AVcLU3AzHQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.5.0.tgz", + "integrity": "sha512-TI9TFfJBfduhcM2GggayNhdYvdJ3UgS/Bu3sB7FB2AUmNCmCJ+TSOT6GXu+bodG5/xL74D5zE4XRaqyjgjsYVQ==", "dev": true, "dependencies": { "@types/estree": "0.0.46" @@ -5211,6 +5220,12 @@ "@types/glob": "^7.1.1" } }, + "node_modules/asar/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -5256,6 +5271,12 @@ "node": ">=4" } }, + "node_modules/async": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", + "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=", + "dev": true + }, "node_modules/async-exit-hook": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-1.1.2.tgz", @@ -6561,10 +6582,13 @@ } }, "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } }, "node_modules/comment-json": { "version": "4.1.1", @@ -7037,13 +7061,20 @@ } }, "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -12240,23 +12271,6 @@ "node": ">= 6.0.0" } }, - "node_modules/puppeteer/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/puppeteer/node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -13394,23 +13408,6 @@ "debug": "^4.3.1" } }, - "node_modules/simple-git/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -14074,9 +14071,9 @@ } }, "node_modules/testcafe": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.15.0.tgz", - "integrity": "sha512-wzuNLXW40UuzlTnI0/m8IFEKuwMM1Vt1IE5jWC8fAbJ6PPjFyM5HyC6Rn8gA0dwe4Bn1IhoybaQezS8tpIkcdg==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", + "integrity": "sha512-G+IqL28PE9wzNKcLjLCjX3z3/KXpzNs0FNC63Y7dXcXx23qlx+yz+Abyh91CIzeiwtXDZX+xMXDYYLtPQLfhlQ==", "dev": true, "dependencies": { "@babel/core": "^7.12.1", @@ -14084,6 +14081,7 @@ "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-decorators": "^7.12.1", "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-async-to-generator": "^7.12.1", @@ -14094,6 +14092,7 @@ "@babel/preset-flow": "^7.12.1", "@babel/preset-react": "^7.12.1", "@babel/runtime": "^7.12.5", + "@miherlosev/esm": "3.2.26", "@types/node": "^12.20.10", "async-exit-hook": "^1.1.2", "babel-plugin-module-resolver": "^4.0.0", @@ -14106,7 +14105,7 @@ "chalk": "^2.3.0", "chrome-remote-interface": "^0.30.0", "coffeescript": "^2.3.1", - "commander": "^2.8.1", + "commander": "^8.0.0", "debug": "^4.3.1", "dedent": "^0.4.0", "del": "^3.0.0", @@ -14152,9 +14151,9 @@ "semver": "^5.6.0", "source-map-support": "^0.5.16", "strip-bom": "^2.0.0", - "testcafe-browser-tools": "2.0.15", - "testcafe-hammerhead": "24.3.1", - "testcafe-legacy-api": "5.0.2", + "testcafe-browser-tools": "2.0.16", + "testcafe-hammerhead": "24.5.7", + "testcafe-legacy-api": "5.1.2", "testcafe-reporter-json": "^2.1.0", "testcafe-reporter-list": "^2.1.0", "testcafe-reporter-minimal": "^2.1.0", @@ -14215,12 +14214,13 @@ } }, "node_modules/testcafe-browser-tools": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.15.tgz", - "integrity": "sha512-bzkh5B1+Ws/I3YZL+9M4TSUq3aAewjvF2oue2l7T7eROIvqwPDE22ZFfPuLew6VIZcotCFZj432s1EgJDFyH7g==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.16.tgz", + "integrity": "sha512-JljbS0FboABksIMEH1L7P4ZdI82AQ8saWb/7WsxkDCOtDuHID5ZSEb/w9tqLN1+4BQaCgS5veN3lWUnfb0saEA==", "dev": true, "dependencies": { "array-find": "^1.0.0", + "debug": "^4.3.1", "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^3.3.0", @@ -14254,6 +14254,23 @@ "node": ">= 8" } }, + "node_modules/testcafe-browser-tools/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/testcafe-browser-tools/node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -14334,12 +14351,15 @@ } }, "node_modules/testcafe-browser-tools/node_modules/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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/testcafe-browser-tools/node_modules/nanoid": { @@ -14412,12 +14432,12 @@ } }, "node_modules/testcafe-hammerhead": { - "version": "24.3.1", - "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-24.3.1.tgz", - "integrity": "sha512-c6eoxfEeLN47/2ToVKP7n8jupUtZijMjIBhhAiVW4HqZ2v+9GNn6wH8GKyWbsmov0hacaYMEvp4mwehJstKjxg==", + "version": "24.5.7", + "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-24.5.7.tgz", + "integrity": "sha512-4pY6GQQaCZAlOolWB2vaYZ/MIpgtmOrZeh4BVbENkbh6DDiPeOxvC0K6yZS5JfINRau5S9mzuNkm2FS7G0urSA==", "dev": true, "dependencies": { - "acorn-hammerhead": "0.4.0", + "acorn-hammerhead": "0.5.0", "asar": "^2.0.1", "bowser": "1.6.0", "brotli": "^1.3.1", @@ -14453,23 +14473,6 @@ "integrity": "sha1-N/w4e2Fstq7zcNq01r1AK3TFxU0=", "dev": true }, - "node_modules/testcafe-hammerhead/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/testcafe-hammerhead/node_modules/iconv-lite": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz", @@ -14564,9 +14567,9 @@ } }, "node_modules/testcafe-legacy-api": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/testcafe-legacy-api/-/testcafe-legacy-api-5.0.2.tgz", - "integrity": "sha512-2BWjCIN5YOUOTyOT4B0wy2TiaJgV8dWhIGpKqE3S34RjNEH62WR+JNhcnh4BSE+btp6H8n1TefcP/AObqSDSDQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/testcafe-legacy-api/-/testcafe-legacy-api-5.1.2.tgz", + "integrity": "sha512-vc9A4rFUdijlBFnNOVMk0hFfxnrAmtA7FMz1P/LtvNyui5JfkLmbyIQcJbxR2rjTINp0owZ2c+xQvYms/us7Fw==", "dev": true, "dependencies": { "async": "0.2.6", @@ -14585,12 +14588,6 @@ "testcafe-hammerhead": ">=19.4.0" } }, - "node_modules/testcafe-legacy-api/node_modules/async": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", - "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=", - "dev": true - }, "node_modules/testcafe-legacy-api/node_modules/dedent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz", @@ -20098,6 +20095,12 @@ "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "dev": true }, + "@miherlosev/esm": { + "version": "3.2.26", + "resolved": "https://registry.npmjs.org/@miherlosev/esm/-/esm-3.2.26.tgz", + "integrity": "sha512-TaW4jTGVE1/ln2VGFChnheMh589QCAZy1MVnLvjjSzZ4pEAa4WYAWPwFkDVZbSdPQdLfZy7LuTyZjWRkhX9/Gg==", + "dev": true + }, "@nodelib/fs.scandir": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", @@ -21091,9 +21094,9 @@ } }, "acorn-hammerhead": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.4.0.tgz", - "integrity": "sha512-zMjPa6kNgMB0zclCZI41sPofSeeHMF9Q6e3ALRsowmmNqoiz1qiJI9oemt9GfZ5e5EmQpElvePT3AVcLU3AzHQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/acorn-hammerhead/-/acorn-hammerhead-0.5.0.tgz", + "integrity": "sha512-TI9TFfJBfduhcM2GggayNhdYvdJ3UgS/Bu3sB7FB2AUmNCmCJ+TSOT6GXu+bodG5/xL74D5zE4XRaqyjgjsYVQ==", "dev": true, "requires": { "@types/estree": "0.0.46" @@ -21512,6 +21515,14 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "tmp-promise": "^1.0.5" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } } }, "asn1": { @@ -21547,6 +21558,12 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", + "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=", + "dev": true + }, "async-exit-hook": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-1.1.2.tgz", @@ -22590,9 +22607,9 @@ } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true }, "comment-json": { @@ -22999,12 +23016,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "decamelize": { @@ -27184,15 +27201,6 @@ "debug": "4" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", @@ -28110,17 +28118,6 @@ "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.3.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - } } }, "sisteransi": { @@ -28659,9 +28656,9 @@ } }, "testcafe": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.15.0.tgz", - "integrity": "sha512-wzuNLXW40UuzlTnI0/m8IFEKuwMM1Vt1IE5jWC8fAbJ6PPjFyM5HyC6Rn8gA0dwe4Bn1IhoybaQezS8tpIkcdg==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/testcafe/-/testcafe-1.17.1.tgz", + "integrity": "sha512-G+IqL28PE9wzNKcLjLCjX3z3/KXpzNs0FNC63Y7dXcXx23qlx+yz+Abyh91CIzeiwtXDZX+xMXDYYLtPQLfhlQ==", "dev": true, "requires": { "@babel/core": "^7.12.1", @@ -28669,6 +28666,7 @@ "@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-decorators": "^7.12.1", "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-transform-async-to-generator": "^7.12.1", @@ -28679,6 +28677,7 @@ "@babel/preset-flow": "^7.12.1", "@babel/preset-react": "^7.12.1", "@babel/runtime": "^7.12.5", + "@miherlosev/esm": "3.2.26", "@types/node": "^12.20.10", "async-exit-hook": "^1.1.2", "babel-plugin-module-resolver": "^4.0.0", @@ -28691,7 +28690,7 @@ "chalk": "^2.3.0", "chrome-remote-interface": "^0.30.0", "coffeescript": "^2.3.1", - "commander": "^2.8.1", + "commander": "^8.0.0", "debug": "^4.3.1", "dedent": "^0.4.0", "del": "^3.0.0", @@ -28737,9 +28736,9 @@ "semver": "^5.6.0", "source-map-support": "^0.5.16", "strip-bom": "^2.0.0", - "testcafe-browser-tools": "2.0.15", - "testcafe-hammerhead": "24.3.1", - "testcafe-legacy-api": "5.0.2", + "testcafe-browser-tools": "2.0.16", + "testcafe-hammerhead": "24.5.7", + "testcafe-legacy-api": "5.1.2", "testcafe-reporter-json": "^2.1.0", "testcafe-reporter-list": "^2.1.0", "testcafe-reporter-minimal": "^2.1.0", @@ -29979,12 +29978,13 @@ } }, "testcafe-browser-tools": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.15.tgz", - "integrity": "sha512-bzkh5B1+Ws/I3YZL+9M4TSUq3aAewjvF2oue2l7T7eROIvqwPDE22ZFfPuLew6VIZcotCFZj432s1EgJDFyH7g==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/testcafe-browser-tools/-/testcafe-browser-tools-2.0.16.tgz", + "integrity": "sha512-JljbS0FboABksIMEH1L7P4ZdI82AQ8saWb/7WsxkDCOtDuHID5ZSEb/w9tqLN1+4BQaCgS5veN3lWUnfb0saEA==", "dev": true, "requires": { "array-find": "^1.0.0", + "debug": "^4.3.1", "dedent": "^0.7.0", "del": "^5.1.0", "execa": "^3.3.0", @@ -30012,6 +30012,15 @@ "which": "^2.0.1" } }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -30074,9 +30083,9 @@ "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==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, "nanoid": { @@ -30133,12 +30142,12 @@ } }, "testcafe-hammerhead": { - "version": "24.3.1", - "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-24.3.1.tgz", - "integrity": "sha512-c6eoxfEeLN47/2ToVKP7n8jupUtZijMjIBhhAiVW4HqZ2v+9GNn6wH8GKyWbsmov0hacaYMEvp4mwehJstKjxg==", + "version": "24.5.7", + "resolved": "https://registry.npmjs.org/testcafe-hammerhead/-/testcafe-hammerhead-24.5.7.tgz", + "integrity": "sha512-4pY6GQQaCZAlOolWB2vaYZ/MIpgtmOrZeh4BVbENkbh6DDiPeOxvC0K6yZS5JfINRau5S9mzuNkm2FS7G0urSA==", "dev": true, "requires": { - "acorn-hammerhead": "0.4.0", + "acorn-hammerhead": "0.5.0", "asar": "^2.0.1", "bowser": "1.6.0", "brotli": "^1.3.1", @@ -30171,15 +30180,6 @@ "integrity": "sha1-N/w4e2Fstq7zcNq01r1AK3TFxU0=", "dev": true }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, "iconv-lite": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz", @@ -30264,9 +30264,9 @@ } }, "testcafe-legacy-api": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/testcafe-legacy-api/-/testcafe-legacy-api-5.0.2.tgz", - "integrity": "sha512-2BWjCIN5YOUOTyOT4B0wy2TiaJgV8dWhIGpKqE3S34RjNEH62WR+JNhcnh4BSE+btp6H8n1TefcP/AObqSDSDQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/testcafe-legacy-api/-/testcafe-legacy-api-5.1.2.tgz", + "integrity": "sha512-vc9A4rFUdijlBFnNOVMk0hFfxnrAmtA7FMz1P/LtvNyui5JfkLmbyIQcJbxR2rjTINp0owZ2c+xQvYms/us7Fw==", "dev": true, "requires": { "async": "0.2.6", @@ -30285,12 +30285,6 @@ "testcafe-hammerhead": ">=19.4.0" }, "dependencies": { - "async": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.6.tgz", - "integrity": "sha1-rT83PZJJrjJIgVZVgryQ4VKrvWg=", - "dev": true - }, "dedent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.6.0.tgz", diff --git a/package.json b/package.json index ccb0e43f1..f0eec53a1 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "puppeteer": "^10.2.0", "serve": "^11.3.2", "simple-git": "^2.24.0", - "testcafe": "^1.15.0", + "testcafe": "^1.17.1", "testcafe-browser-provider-browserstack": "^1.13.1", "underscore.string": "^3.3.5", "urijs": "1.18.12", diff --git a/tests/acceptance/index.js b/tests/acceptance/index.js index 2f4ff8da6..8ce3bec22 100644 --- a/tests/acceptance/index.js +++ b/tests/acceptance/index.js @@ -31,7 +31,7 @@ runTests(argv.browsers, argv.concurrency); */ async function runTests (browsers, concurrency) { const testcafe = await createTestCafe({ - configFile: './testcafe.json' + configFile: './tests/acceptance/testcafe.json' }); try { const numberTestsFailed = await testcafe.createRunner()