happy-dom 16.2.9 → 16.4.0

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.
Files changed (59) hide show
  1. package/cjs/browser/types/IBrowserSettings.d.ts +2 -0
  2. package/cjs/browser/types/IBrowserSettings.d.ts.map +1 -1
  3. package/cjs/browser/types/IOptionalBrowserSettings.d.ts +2 -0
  4. package/cjs/browser/types/IOptionalBrowserSettings.d.ts.map +1 -1
  5. package/cjs/fetch/Fetch.cjs +29 -4
  6. package/cjs/fetch/Fetch.cjs.map +1 -1
  7. package/cjs/fetch/Fetch.d.ts +1 -0
  8. package/cjs/fetch/Fetch.d.ts.map +1 -1
  9. package/cjs/fetch/SyncFetch.cjs +28 -2
  10. package/cjs/fetch/SyncFetch.cjs.map +1 -1
  11. package/cjs/fetch/SyncFetch.d.ts +1 -0
  12. package/cjs/fetch/SyncFetch.d.ts.map +1 -1
  13. package/cjs/fetch/types/IFetchInterceptor.cjs +3 -0
  14. package/cjs/fetch/types/IFetchInterceptor.cjs.map +1 -0
  15. package/cjs/fetch/types/IFetchInterceptor.d.ts +57 -0
  16. package/cjs/fetch/types/IFetchInterceptor.d.ts.map +1 -0
  17. package/cjs/index.cjs.map +1 -1
  18. package/cjs/index.d.ts +3 -1
  19. package/cjs/index.d.ts.map +1 -1
  20. package/cjs/query-selector/SelectorItem.cjs +11 -0
  21. package/cjs/query-selector/SelectorItem.cjs.map +1 -1
  22. package/cjs/query-selector/SelectorItem.d.ts.map +1 -1
  23. package/cjs/query-selector/SelectorParser.cjs +11 -1
  24. package/cjs/query-selector/SelectorParser.cjs.map +1 -1
  25. package/cjs/query-selector/SelectorParser.d.ts.map +1 -1
  26. package/lib/browser/types/IBrowserSettings.d.ts +2 -0
  27. package/lib/browser/types/IBrowserSettings.d.ts.map +1 -1
  28. package/lib/browser/types/IOptionalBrowserSettings.d.ts +2 -0
  29. package/lib/browser/types/IOptionalBrowserSettings.d.ts.map +1 -1
  30. package/lib/fetch/Fetch.d.ts +1 -0
  31. package/lib/fetch/Fetch.d.ts.map +1 -1
  32. package/lib/fetch/Fetch.js +29 -4
  33. package/lib/fetch/Fetch.js.map +1 -1
  34. package/lib/fetch/SyncFetch.d.ts +1 -0
  35. package/lib/fetch/SyncFetch.d.ts.map +1 -1
  36. package/lib/fetch/SyncFetch.js +28 -2
  37. package/lib/fetch/SyncFetch.js.map +1 -1
  38. package/lib/fetch/types/IFetchInterceptor.d.ts +57 -0
  39. package/lib/fetch/types/IFetchInterceptor.d.ts.map +1 -0
  40. package/lib/fetch/types/IFetchInterceptor.js +2 -0
  41. package/lib/fetch/types/IFetchInterceptor.js.map +1 -0
  42. package/lib/index.d.ts +3 -1
  43. package/lib/index.d.ts.map +1 -1
  44. package/lib/index.js.map +1 -1
  45. package/lib/query-selector/SelectorItem.d.ts.map +1 -1
  46. package/lib/query-selector/SelectorItem.js +11 -0
  47. package/lib/query-selector/SelectorItem.js.map +1 -1
  48. package/lib/query-selector/SelectorParser.d.ts.map +1 -1
  49. package/lib/query-selector/SelectorParser.js +11 -1
  50. package/lib/query-selector/SelectorParser.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/browser/types/IBrowserSettings.ts +3 -0
  53. package/src/browser/types/IOptionalBrowserSettings.ts +3 -0
  54. package/src/fetch/Fetch.ts +32 -5
  55. package/src/fetch/SyncFetch.ts +29 -2
  56. package/src/fetch/types/IFetchInterceptor.ts +60 -0
  57. package/src/index.ts +4 -0
  58. package/src/query-selector/SelectorItem.ts +10 -0
  59. package/src/query-selector/SelectorParser.ts +11 -4
@@ -20,6 +20,7 @@ import Zlib from 'zlib';
20
20
  import FetchResponseRedirectUtility from './utilities/FetchResponseRedirectUtility.js';
21
21
  import FetchCORSUtility from './utilities/FetchCORSUtility.js';
22
22
  import Fetch from './Fetch.js';
23
+ import IFetchInterceptor from './types/IFetchInterceptor.js';
23
24
 
24
25
  interface ISyncHTTPResponse {
25
26
  error: string;
@@ -39,6 +40,7 @@ export default class SyncFetch {
39
40
  private redirectCount = 0;
40
41
  private disableCache: boolean;
41
42
  private disableSameOriginPolicy: boolean;
43
+ private interceptor?: IFetchInterceptor;
42
44
  #browserFrame: IBrowserFrame;
43
45
  #window: BrowserWindow;
44
46
  #unfilteredHeaders: Headers | null = null;
@@ -84,6 +86,7 @@ export default class SyncFetch {
84
86
  options.disableSameOriginPolicy ??
85
87
  this.#browserFrame.page.context.browser.settings.fetch.disableSameOriginPolicy ??
86
88
  false;
89
+ this.interceptor = this.#browserFrame.page.context.browser.settings.fetch.interceptor;
87
90
  }
88
91
 
89
92
  /**
@@ -93,6 +96,15 @@ export default class SyncFetch {
93
96
  */
94
97
  public send(): ISyncResponse {
95
98
  FetchRequestReferrerUtility.prepareRequest(new URL(this.#window.location.href), this.request);
99
+ const beforeRequestResponse = this.interceptor?.beforeSyncRequest
100
+ ? this.interceptor.beforeSyncRequest({
101
+ request: this.request,
102
+ window: this.#window
103
+ })
104
+ : undefined;
105
+ if (typeof beforeRequestResponse === 'object') {
106
+ return beforeRequestResponse;
107
+ }
96
108
  FetchRequestValidationUtility.validateSchema(this.request);
97
109
 
98
110
  if (this.request.signal.aborted) {
@@ -104,7 +116,7 @@ export default class SyncFetch {
104
116
 
105
117
  if (this.request[PropertySymbol.url].protocol === 'data:') {
106
118
  const result = DataURIParser.parse(this.request.url);
107
- return {
119
+ const response = {
108
120
  status: 200,
109
121
  statusText: 'OK',
110
122
  ok: true,
@@ -113,6 +125,14 @@ export default class SyncFetch {
113
125
  headers: new Headers({ 'Content-Type': result.type }),
114
126
  body: result.buffer
115
127
  };
128
+ const interceptedResponse = this.interceptor?.afterSyncResponse
129
+ ? this.interceptor.afterSyncResponse({
130
+ window: this.#window,
131
+ response,
132
+ request: this.request
133
+ })
134
+ : undefined;
135
+ return typeof interceptedResponse === 'object' ? interceptedResponse : response;
116
136
  }
117
137
 
118
138
  // Security check for "https" to "http" requests.
@@ -416,7 +436,14 @@ export default class SyncFetch {
416
436
  });
417
437
  }
418
438
 
419
- return redirectedResponse;
439
+ const interceptedResponse = this.interceptor?.afterSyncResponse
440
+ ? this.interceptor.afterSyncResponse({
441
+ window: this.#window,
442
+ response: redirectedResponse,
443
+ request: this.request
444
+ })
445
+ : undefined;
446
+ return typeof interceptedResponse === 'object' ? interceptedResponse : redirectedResponse;
420
447
  }
421
448
 
422
449
  /**
@@ -0,0 +1,60 @@
1
+ import Request from '../Request.js';
2
+ import BrowserWindow from '../../window/BrowserWindow.js';
3
+ import Response from '../Response.js';
4
+ import ISyncResponse from './ISyncResponse.js';
5
+
6
+ export default interface IFetchInterceptor {
7
+ /**
8
+ * Hook dispatched before making an async request.
9
+ * It can be used for modifying the request, providing a response without making a request or for logging.
10
+ *
11
+ * @param context Contains the request and the window from where the request was made.
12
+ *
13
+ * @returns Promise that can resolve to a response to be used instead of sending out the response.
14
+ */
15
+ beforeAsyncRequest?: (context: {
16
+ request: Request;
17
+ window: BrowserWindow;
18
+ }) => Promise<Response | void>;
19
+
20
+ /**
21
+ * Hook dispatched before making an sync request.
22
+ * It can be used for modifying the request, providing a response without making a request or for logging.
23
+ *
24
+ * @param context Contains the request and the window from where the request was made.
25
+ *
26
+ * @returns Promise that can resolve to a response to be used instead of sending out the response.
27
+ */
28
+ beforeSyncRequest?: (context: {
29
+ request: Request;
30
+ window: BrowserWindow;
31
+ }) => ISyncResponse | void;
32
+
33
+ /**
34
+ * Hook dispatched after receiving an async response.
35
+ * It can be used for modifying or replacing the response and logging.
36
+ *
37
+ * @param context Contains the request, response and the window from where the request was made.
38
+ *
39
+ * @returns Promise that can resolve to a response to be used instead of sending out the response.
40
+ */
41
+ afterAsyncResponse?: (context: {
42
+ request: Request;
43
+ response: Response;
44
+ window: BrowserWindow;
45
+ }) => Promise<Response | void>;
46
+
47
+ /**
48
+ * Hook dispatched after receiving a sync response.
49
+ * It can be used for modifying or replacing the response and logging
50
+ *
51
+ * @param context Contains the request, response and the window from where the request was made.
52
+ *
53
+ * @returns Promise that can resolve to a response to be used instead of sending out the response.
54
+ */
55
+ afterSyncResponse?: (context: {
56
+ request: Request;
57
+ response: ISyncResponse;
58
+ window: BrowserWindow;
59
+ }) => ISyncResponse | void;
60
+ }
package/src/index.ts CHANGED
@@ -57,6 +57,8 @@ import AbortSignal from './fetch/AbortSignal.js';
57
57
  import Headers from './fetch/Headers.js';
58
58
  import Request from './fetch/Request.js';
59
59
  import Response from './fetch/Response.js';
60
+ import IFetchInterceptor from './fetch/types/IFetchInterceptor.js';
61
+ import ISyncResponse from './fetch/types/ISyncResponse.js';
60
62
  import Blob from './file/Blob.js';
61
63
  import File from './file/File.js';
62
64
  import FileReader from './file/FileReader.js';
@@ -206,6 +208,8 @@ import type ITouchEventInit from './event/events/ITouchEventInit.js';
206
208
  import type IWheelEventInit from './event/events/IWheelEventInit.js';
207
209
 
208
210
  export type {
211
+ IFetchInterceptor,
212
+ ISyncResponse,
209
213
  IAnimationEventInit,
210
214
  IBrowser,
211
215
  IBrowserContext,
@@ -340,6 +340,16 @@ export default class SelectorItem {
340
340
  priorityWeightForHas = match.priorityWeight;
341
341
  }
342
342
  }
343
+ } else if (pseudo.arguments[0] === '>') {
344
+ for (const selectorItem of pseudo.selectorItems) {
345
+ for (const child of element[PropertySymbol.elementArray]) {
346
+ const match = selectorItem.match(child);
347
+ if (match && priorityWeightForHas < match.priorityWeight) {
348
+ priorityWeightForHas = match.priorityWeight;
349
+ break;
350
+ }
351
+ }
352
+ }
343
353
  } else {
344
354
  for (const selectorItem of pseudo.selectorItems) {
345
355
  const match = this.matchChildOfElement(selectorItem, element);
@@ -277,6 +277,10 @@ export default class SelectorParser {
277
277
  ): ISelectorPseudo {
278
278
  const lowerName = name.toLowerCase();
279
279
 
280
+ if (args) {
281
+ args = args.trim();
282
+ }
283
+
280
284
  if (!args) {
281
285
  return { name: lowerName, arguments: null, selectorItems: null, nthFunction: null };
282
286
  }
@@ -328,10 +332,13 @@ export default class SelectorParser {
328
332
 
329
333
  // The ":has()" pseudo selector doesn't allow for it to be nested inside another ":has()" pseudo selector, as it can lead to cyclic querying.
330
334
  if (!args.includes(':has(')) {
331
- for (const group of this.getSelectorGroups(
332
- args[0] === '+' ? args.replace('+', '') : args,
333
- options
334
- )) {
335
+ let newArgs = args;
336
+ if (args[0] === '+') {
337
+ newArgs = args.replace('+', '');
338
+ } else if (args[0] === '>') {
339
+ newArgs = args.replace('>', '');
340
+ }
341
+ for (const group of this.getSelectorGroups(newArgs, options)) {
335
342
  hasSelectorItems.push(group[0]);
336
343
  }
337
344
  }