lighthouse 9.5.0-dev.20220613 → 9.5.0-dev.20220616

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -108,13 +108,64 @@ class UserFlow {
108
108
  */
109
109
  async navigate(requestor, stepOptions) {
110
110
  if (this.currentTimespan) throw new Error('Timespan already in progress');
111
+ if (this.currentNavigation) throw new Error('Navigation already in progress');
111
112
 
112
113
  const options = this._getNextNavigationOptions(stepOptions);
113
114
  const gatherResult = await navigationGather(requestor, options);
114
115
 
115
116
  this._addGatherStep(gatherResult, options);
117
+ }
118
+
119
+ /**
120
+ * This is an alternative to `navigate()` that can be used to analyze a navigation triggered by user interaction.
121
+ * For more on user triggered navigations, see https://github.com/GoogleChrome/lighthouse/blob/master/docs/user-flows.md#triggering-a-navigation-via-user-interactions.
122
+ *
123
+ * @param {StepOptions=} stepOptions
124
+ */
125
+ async startNavigation(stepOptions) {
126
+ /** @type {(value: () => void) => void} */
127
+ let completeSetup;
128
+ /** @type {(value: any) => void} */
129
+ let rejectDuringSetup;
130
+
131
+ // This promise will resolve once the setup is done
132
+ // and Lighthouse is waiting for a page navigation to be triggered.
133
+ const navigationSetupPromise = new Promise((resolve, reject) => {
134
+ completeSetup = resolve;
135
+ rejectDuringSetup = reject;
136
+ });
137
+
138
+ // The promise in this callback will not resolve until `continueNavigation` is invoked,
139
+ // because `continueNavigation` is passed along to `navigateSetupPromise`
140
+ // and extracted into `continueAndAwaitResult` below.
141
+ const navigationResultPromise = this.navigate(
142
+ () => new Promise(continueNavigation => completeSetup(continueNavigation)),
143
+ stepOptions
144
+ ).catch(err => {
145
+ if (this.currentNavigation) {
146
+ // If the navigation already started, re-throw the error so it is emitted when `navigationResultPromise` is awaited.
147
+ throw err;
148
+ } else {
149
+ // If the navigation has not started, reject the `navigationSetupPromise` so the error throws when it is awaited in `startNavigation`.
150
+ rejectDuringSetup(err);
151
+ }
152
+ });
153
+
154
+ const continueNavigation = await navigationSetupPromise;
116
155
 
117
- return gatherResult;
156
+ async function continueAndAwaitResult() {
157
+ continueNavigation();
158
+ await navigationResultPromise;
159
+ }
160
+
161
+ this.currentNavigation = {continueAndAwaitResult};
162
+ }
163
+
164
+ async endNavigation() {
165
+ if (this.currentTimespan) throw new Error('Timespan already in progress');
166
+ if (!this.currentNavigation) throw new Error('No navigation in progress');
167
+ await this.currentNavigation.continueAndAwaitResult();
168
+ this.currentNavigation = undefined;
118
169
  }
119
170
 
120
171
  /**
@@ -122,6 +173,7 @@ class UserFlow {
122
173
  */
123
174
  async startTimespan(stepOptions) {
124
175
  if (this.currentTimespan) throw new Error('Timespan already in progress');
176
+ if (this.currentNavigation) throw new Error('Navigation already in progress');
125
177
 
126
178
  const options = {...this.options, ...stepOptions};
127
179
  const timespan = await startTimespanGather(options);
@@ -130,14 +182,13 @@ class UserFlow {
130
182
 
131
183
  async endTimespan() {
132
184
  if (!this.currentTimespan) throw new Error('No timespan in progress');
185
+ if (this.currentNavigation) throw new Error('Navigation already in progress');
133
186
 
134
187
  const {timespan, options} = this.currentTimespan;
135
188
  const gatherResult = await timespan.endTimespanGather();
136
189
  this.currentTimespan = undefined;
137
190
 
138
191
  this._addGatherStep(gatherResult, options);
139
-
140
- return gatherResult;
141
192
  }
142
193
 
143
194
  /**
@@ -145,13 +196,12 @@ class UserFlow {
145
196
  */
146
197
  async snapshot(stepOptions) {
147
198
  if (this.currentTimespan) throw new Error('Timespan already in progress');
199
+ if (this.currentNavigation) throw new Error('Navigation already in progress');
148
200
 
149
201
  const options = {...this.options, ...stepOptions};
150
202
  const gatherResult = await snapshotGather(options);
151
203
 
152
204
  this._addGatherStep(gatherResult, options);
153
-
154
- return gatherResult;
155
205
  }
156
206
 
157
207
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lighthouse",
3
- "version": "9.5.0-dev.20220613",
3
+ "version": "9.5.0-dev.20220616",
4
4
  "description": "Automated auditing, performance metrics, and best practices for the web.",
5
5
  "main": "./lighthouse-core/index.js",
6
6
  "bin": {
@@ -133,9 +133,13 @@ export class DOM {
133
133
  const element = this.createElement('span');
134
134
 
135
135
  for (const segment of Util.splitMarkdownLink(text)) {
136
+ const processedSegment = segment.text.includes('`') ?
137
+ this.convertMarkdownCodeSnippets(segment.text) :
138
+ segment.text;
139
+
136
140
  if (!segment.isLink) {
137
141
  // Plain text segment.
138
- element.append(this._document.createTextNode(segment.text));
142
+ element.append(processedSegment);
139
143
  continue;
140
144
  }
141
145
 
@@ -151,7 +155,7 @@ export class DOM {
151
155
  const a = this.createElement('a');
152
156
  a.rel = 'noopener';
153
157
  a.target = '_blank';
154
- a.textContent = segment.text;
158
+ a.append(processedSegment);
155
159
  this.safelySetHref(a, url.href);
156
160
  element.append(a);
157
161
  }
@@ -96,6 +96,21 @@ describe('DOM', () => {
96
96
  'and some text afterwards.', 'link with spaces in brackets');
97
97
  });
98
98
 
99
+ it('correctly converts code snippets', () => {
100
+ let result = dom.convertMarkdownLinkSnippets(
101
+ 'Some `code`. [Learn more](http://example.com).');
102
+ assert.equal(result.innerHTML,
103
+ '<span>Some <code>code</code>. </span>' +
104
+ '<a rel="noopener" target="_blank" href="http://example.com/">Learn more</a>.');
105
+
106
+ result = dom.convertMarkdownLinkSnippets(
107
+ '[link with `code`](https://example.com/foo) and some text afterwards.');
108
+ assert.equal(result.innerHTML,
109
+ '<a rel="noopener" target="_blank" href="https://example.com/foo">' +
110
+ '<span>link with <code>code</code></span>' +
111
+ '</a> and some text afterwards.', 'link with code snippet inside');
112
+ });
113
+
99
114
  it('handles invalid urls', () => {
100
115
  const text = 'Text has [bad](https:///) link.';
101
116
  assert.throws(() => {
@@ -120,8 +135,9 @@ describe('DOM', () => {
120
135
  const text = 'Ensuring `<td>` cells using the `[headers]` are good. ' +
121
136
  '[Learn more](https://dequeuniversity.com/rules/axe/3.1/td-headers-attr).';
122
137
  const result = dom.convertMarkdownLinkSnippets(text);
123
- assert.equal(result.innerHTML, 'Ensuring `&lt;td&gt;` cells using the `[headers]` are ' +
124
- 'good. <a rel="noopener" target="_blank" href="https://dequeuniversity.com/rules/axe/3.1/td-headers-attr">Learn more</a>.');
138
+ assert.equal(result.innerHTML,
139
+ '<span>Ensuring <code>&lt;td&gt;</code> cells using the <code>[headers]</code> are ' +
140
+ 'good. </span><a rel="noopener" target="_blank" href="https://dequeuniversity.com/rules/axe/3.1/td-headers-attr">Learn more</a>.');
125
141
  });
126
142
 
127
143
  it('appends utm params to the URLs with https://developers.google.com origin', () => {