happy-dom 14.1.1 → 14.2.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.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

@@ -97,8 +97,12 @@ export default class SelectorItem {
97
97
  }
98
98
 
99
99
  // Pseudo match
100
- if (this.pseudos && !this.matchPsuedo(element)) {
101
- return null;
100
+ if (this.pseudos) {
101
+ const result = this.matchPsuedo(element);
102
+ if (!result) {
103
+ return null;
104
+ }
105
+ priorityWeight += result.priorityWeight;
102
106
  }
103
107
 
104
108
  return { priorityWeight };
@@ -110,16 +114,18 @@ export default class SelectorItem {
110
114
  * @param element Element.
111
115
  * @returns Result.
112
116
  */
113
- private matchPsuedo(element: Element): boolean {
117
+ private matchPsuedo(element: Element): ISelectorMatch | null {
114
118
  const parent = <Element>element[PropertySymbol.parentNode];
115
119
  const parentChildren = element[PropertySymbol.parentNode]
116
120
  ? (<Element>element[PropertySymbol.parentNode])[PropertySymbol.children]
117
121
  : [];
118
122
 
119
123
  if (!this.pseudos) {
120
- return true;
124
+ return { priorityWeight: 0 };
121
125
  }
122
126
 
127
+ let priorityWeight = 0;
128
+
123
129
  for (const pseudo of this.pseudos) {
124
130
  // Validation
125
131
  switch (pseudo.name) {
@@ -147,16 +153,20 @@ export default class SelectorItem {
147
153
  case 'nth-of-type':
148
154
  case 'nth-last-child':
149
155
  case 'nth-last-of-type':
150
- return false;
156
+ return null;
151
157
  }
152
158
  }
153
159
 
154
- if (!this.matchPseudoItem(element, parentChildren, pseudo)) {
155
- return false;
160
+ const selectorMatch = this.matchPseudoItem(element, parentChildren, pseudo);
161
+
162
+ if (!selectorMatch) {
163
+ return null;
156
164
  }
165
+
166
+ priorityWeight += selectorMatch.priorityWeight;
157
167
  }
158
168
 
159
- return true;
169
+ return { priorityWeight };
160
170
  }
161
171
 
162
172
  /**
@@ -170,83 +180,113 @@ export default class SelectorItem {
170
180
  element: Element,
171
181
  parentChildren: Element[],
172
182
  pseudo: ISelectorPseudo
173
- ): boolean {
183
+ ): ISelectorMatch | null {
174
184
  switch (pseudo.name) {
175
185
  case 'first-child':
176
- return parentChildren[0] === element;
186
+ return parentChildren[0] === element ? { priorityWeight: 10 } : null;
177
187
  case 'last-child':
178
- return parentChildren.length && parentChildren[parentChildren.length - 1] === element;
188
+ return parentChildren.length && parentChildren[parentChildren.length - 1] === element
189
+ ? { priorityWeight: 10 }
190
+ : null;
179
191
  case 'only-child':
180
- return parentChildren.length === 1 && parentChildren[0] === element;
192
+ return parentChildren.length === 1 && parentChildren[0] === element
193
+ ? { priorityWeight: 10 }
194
+ : null;
181
195
  case 'first-of-type':
182
196
  for (const child of parentChildren) {
183
197
  if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
184
- return child === element;
198
+ return child === element ? { priorityWeight: 10 } : null;
185
199
  }
186
200
  }
187
- return false;
201
+ return null;
188
202
  case 'last-of-type':
189
203
  for (let i = parentChildren.length - 1; i >= 0; i--) {
190
204
  const child = parentChildren[i];
191
205
  if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
192
- return child === element;
206
+ return child === element ? { priorityWeight: 10 } : null;
193
207
  }
194
208
  }
195
- return false;
209
+ return null;
196
210
  case 'only-of-type':
197
211
  let isFound = false;
198
212
  for (const child of parentChildren) {
199
213
  if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
200
214
  if (isFound || child !== element) {
201
- return false;
215
+ return null;
202
216
  }
203
217
  isFound = true;
204
218
  }
205
219
  }
206
- return isFound;
220
+ return isFound ? { priorityWeight: 10 } : null;
207
221
  case 'checked':
208
- return element[PropertySymbol.tagName] === 'INPUT' && (<HTMLInputElement>element).checked;
222
+ return element[PropertySymbol.tagName] === 'INPUT' && (<HTMLInputElement>element).checked
223
+ ? { priorityWeight: 10 }
224
+ : null;
209
225
  case 'empty':
210
- return !(<Element>element)[PropertySymbol.children].length;
226
+ return !(<Element>element)[PropertySymbol.children].length ? { priorityWeight: 10 } : null;
211
227
  case 'root':
212
- return element[PropertySymbol.tagName] === 'HTML';
228
+ return element[PropertySymbol.tagName] === 'HTML' ? { priorityWeight: 10 } : null;
213
229
  case 'not':
214
- return !pseudo.selectorItem.match(element);
230
+ return !pseudo.selectorItems[0].match(element) ? { priorityWeight: 10 } : null;
215
231
  case 'nth-child':
216
- const nthChildIndex = pseudo.selectorItem
217
- ? parentChildren.filter((child) => pseudo.selectorItem.match(child)).indexOf(element)
232
+ const nthChildIndex = pseudo.selectorItems[0]
233
+ ? parentChildren.filter((child) => pseudo.selectorItems[0].match(child)).indexOf(element)
218
234
  : parentChildren.indexOf(element);
219
- return nthChildIndex !== -1 && pseudo.nthFunction(nthChildIndex + 1);
235
+ return nthChildIndex !== -1 && pseudo.nthFunction(nthChildIndex + 1)
236
+ ? { priorityWeight: 10 }
237
+ : null;
220
238
  case 'nth-of-type':
221
239
  if (!element[PropertySymbol.parentNode]) {
222
- return false;
240
+ return null;
223
241
  }
224
242
  const nthOfTypeIndex = parentChildren
225
243
  .filter((child) => child[PropertySymbol.tagName] === element[PropertySymbol.tagName])
226
244
  .indexOf(element);
227
- return nthOfTypeIndex !== -1 && pseudo.nthFunction(nthOfTypeIndex + 1);
245
+ return nthOfTypeIndex !== -1 && pseudo.nthFunction(nthOfTypeIndex + 1)
246
+ ? { priorityWeight: 10 }
247
+ : null;
228
248
  case 'nth-last-child':
229
- const nthLastChildIndex = pseudo.selectorItem
249
+ const nthLastChildIndex = pseudo.selectorItems[0]
230
250
  ? parentChildren
231
- .filter((child) => pseudo.selectorItem.match(child))
251
+ .filter((child) => pseudo.selectorItems[0].match(child))
232
252
  .reverse()
233
253
  .indexOf(element)
234
254
  : parentChildren.reverse().indexOf(element);
235
- return nthLastChildIndex !== -1 && pseudo.nthFunction(nthLastChildIndex + 1);
255
+ return nthLastChildIndex !== -1 && pseudo.nthFunction(nthLastChildIndex + 1)
256
+ ? { priorityWeight: 10 }
257
+ : null;
236
258
  case 'nth-last-of-type':
237
259
  const nthLastOfTypeIndex = parentChildren
238
260
  .filter((child) => child[PropertySymbol.tagName] === element[PropertySymbol.tagName])
239
261
  .reverse()
240
262
  .indexOf(element);
241
- return nthLastOfTypeIndex !== -1 && pseudo.nthFunction(nthLastOfTypeIndex + 1);
263
+ return nthLastOfTypeIndex !== -1 && pseudo.nthFunction(nthLastOfTypeIndex + 1)
264
+ ? { priorityWeight: 10 }
265
+ : null;
242
266
  case 'target':
243
267
  const hash = element[PropertySymbol.ownerDocument].location.hash;
244
268
  if (!hash) {
245
- return false;
269
+ return null;
270
+ }
271
+ return element.isConnected && element.id === hash.slice(1) ? { priorityWeight: 10 } : null;
272
+ case 'is':
273
+ let priorityWeight = 0;
274
+ for (const selectorItem of pseudo.selectorItems) {
275
+ const match = selectorItem.match(element);
276
+ if (match) {
277
+ priorityWeight = match.priorityWeight;
278
+ }
246
279
  }
247
- return element.isConnected && element.id === hash.slice(1);
280
+ return priorityWeight ? { priorityWeight } : null;
281
+ case 'where':
282
+ for (const selectorItem of pseudo.selectorItems) {
283
+ if (selectorItem.match(element)) {
284
+ return { priorityWeight: 0 };
285
+ }
286
+ }
287
+ return null;
248
288
  default:
249
- return false;
289
+ return null;
250
290
  }
251
291
  }
252
292
 
@@ -247,7 +247,7 @@ export default class SelectorParser {
247
247
  const lowerName = name.toLowerCase();
248
248
 
249
249
  if (!args) {
250
- return { name: lowerName, arguments: null, selectorItem: null, nthFunction: null };
250
+ return { name: lowerName, arguments: null, selectorItems: null, nthFunction: null };
251
251
  }
252
252
 
253
253
  switch (lowerName) {
@@ -260,7 +260,7 @@ export default class SelectorParser {
260
260
  return {
261
261
  name: lowerName,
262
262
  arguments: args,
263
- selectorItem,
263
+ selectorItems: [selectorItem],
264
264
  nthFunction: this.getPseudoNthFunction(nthFunction)
265
265
  };
266
266
  case 'nth-of-type':
@@ -268,18 +268,31 @@ export default class SelectorParser {
268
268
  return {
269
269
  name: lowerName,
270
270
  arguments: args,
271
- selectorItem: null,
271
+ selectorItems: null,
272
272
  nthFunction: this.getPseudoNthFunction(args)
273
273
  };
274
274
  case 'not':
275
275
  return {
276
276
  name: lowerName,
277
277
  arguments: args,
278
- selectorItem: this.getSelectorItem(args),
278
+ selectorItems: [this.getSelectorItem(args)],
279
+ nthFunction: null
280
+ };
281
+ case 'is':
282
+ case 'where':
283
+ const selectorGroups = this.getSelectorGroups(args);
284
+ const selectorItems = [];
285
+ for (const group of selectorGroups) {
286
+ selectorItems.push(group[0]);
287
+ }
288
+ return {
289
+ name: lowerName,
290
+ arguments: args,
291
+ selectorItems,
279
292
  nthFunction: null
280
293
  };
281
294
  default:
282
- return { name: lowerName, arguments: args, selectorItem: null, nthFunction: null };
295
+ return { name: lowerName, arguments: args, selectorItems: null, nthFunction: null };
283
296
  }
284
297
  }
285
298
 
@@ -1,4 +1,6 @@
1
1
  import * as PropertySymbol from '../PropertySymbol.js';
2
+ import { IOptionalBrowserSettings } from '../index.js';
3
+ import BrowserWindow from './BrowserWindow.js';
2
4
  import Window from './Window.js';
3
5
  import { Buffer } from 'buffer';
4
6
 
@@ -70,6 +72,61 @@ export default class GlobalWindow extends Window {
70
72
  public gc: () => void = globalThis.gc;
71
73
  public v8debug?: unknown = globalThis.v8debug;
72
74
 
75
+ /**
76
+ * Constructor.
77
+ *
78
+ * @param [options] Options.
79
+ * @param [options.width] Window width. Defaults to "1024".
80
+ * @param [options.height] Window height. Defaults to "768".
81
+ * @param [options.innerWidth] Inner width. Deprecated. Defaults to "1024".
82
+ * @param [options.innerHeight] Inner height. Deprecated. Defaults to "768".
83
+ * @param [options.url] URL.
84
+ * @param [options.console] Console.
85
+ * @param [options.settings] Settings.
86
+ */
87
+ constructor(options?: {
88
+ width?: number;
89
+ height?: number;
90
+ /** @deprecated Replaced by the "width" property. */
91
+ innerWidth?: number;
92
+ /** @deprecated Replaced by the "height" property. */
93
+ innerHeight?: number;
94
+ url?: string;
95
+ console?: Console;
96
+ settings?: IOptionalBrowserSettings;
97
+ }) {
98
+ super(options);
99
+
100
+ /**
101
+ * Binds getts and setters, so that they will appear as an "own" property when using Object.getOwnPropertyNames().
102
+ *
103
+ * This is needed for Vitest to work as it relies on Object.getOwnPropertyNames() to get the list of properties.
104
+ *
105
+ * @see https://github.com/capricorn86/happy-dom/issues/1339
106
+ */
107
+ for (const windowClass of [GlobalWindow, Window, BrowserWindow]) {
108
+ const propertyDescriptors = Object.getOwnPropertyDescriptors(
109
+ Reflect.getPrototypeOf(windowClass.prototype)
110
+ );
111
+
112
+ for (const key of Object.keys(propertyDescriptors)) {
113
+ const windowPropertyDescriptor = propertyDescriptors[key];
114
+ if (windowPropertyDescriptor.get || windowPropertyDescriptor.set) {
115
+ const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(this, key);
116
+
117
+ if (!ownPropertyDescriptor) {
118
+ Object.defineProperty(this, key, {
119
+ configurable: true,
120
+ enumerable: windowPropertyDescriptor.enumerable,
121
+ get: windowPropertyDescriptor.get?.bind(this),
122
+ set: windowPropertyDescriptor.set?.bind(this)
123
+ });
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+
73
130
  /**
74
131
  * Setup of VM context.
75
132
  */