From 595cf86f921461918bffc727534c2721c14e7a15 Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Thu, 20 Nov 2025 20:31:55 -0800 Subject: [PATCH] =?UTF-8?q?Adds=20experimental=20=E2=80=9Cdecorators?= =?UTF-8?q?=E2=80=9D=20interface.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TC39 proposals for JavaScript decorators and decorator metadata have made their way to stage 3. The next step is for browsers to begin implementing. It is not an “if” anymore, it is a “when”. This means that it’s time for us to explore what ergonomic improvements might be unlocked by enabling interoperation with these new interfaces. Some important notes: - We could ship this now (only integrator code requires new syntax). - The new interface _should_ be more idiomatic. - We should be able to delete some invented concepts entirely. Closes #346. --- .babelrc.json | 5 + CLAUDE.md | 3 +- DECORATORS.md | 788 ++++++++ eslint.config.js | 20 +- package-lock.json | 1053 +++++++++- package.json | 6 + test-next/index.html | 18 + test-next/index.js | 23 + test-next/test-analysis-errors.html | 18 + test-next/test-analysis-errors.js | 1031 ++++++++++ test-next/test-analysis-errors.src.js | 661 +++++++ test-next/test-attribute-changed-errors.html | 18 + test-next/test-attribute-changed-errors.js | 71 + .../test-attribute-changed-errors.src.js | 57 + test-next/test-basic-properties.html | 18 + test-next/test-basic-properties.js | 703 +++++++ test-next/test-basic-properties.src.js | 543 ++++++ test-next/test-computed-properties.html | 18 + test-next/test-computed-properties.js | 382 ++++ test-next/test-computed-properties.src.js | 315 +++ test-next/test-default-properties.html | 18 + test-next/test-default-properties.js | 289 +++ test-next/test-default-properties.src.js | 207 ++ test-next/test-element-upgrade.html | 18 + test-next/test-element-upgrade.js | 114 ++ test-next/test-element-upgrade.src.js | 161 ++ test-next/test-inheritance.html | 18 + test-next/test-inheritance.js | 679 +++++++ test-next/test-inheritance.src.js | 494 +++++ test-next/test-initial-properties.html | 18 + test-next/test-initial-properties.js | 222 +++ test-next/test-initial-properties.src.js | 167 ++ test-next/test-initialization-errors.html | 18 + test-next/test-initialization-errors.js | 181 ++ test-next/test-initialization-errors.src.js | 137 ++ test-next/test-internal-properties.html | 18 + test-next/test-internal-properties.js | 318 ++++ test-next/test-internal-properties.src.js | 294 +++ test-next/test-listeners.html | 18 + test-next/test-listeners.js | 542 ++++++ test-next/test-listeners.src.js | 463 +++++ test-next/test-observed-properties.html | 18 + test-next/test-observed-properties.js | 315 +++ test-next/test-observed-properties.src.js | 230 +++ test-next/test-parser.html | 18 + test-next/test-parser.js | 1686 +++++++++++++++++ test-next/test-property-deletion.html | 18 + test-next/test-property-deletion.js | 125 ++ test-next/test-property-deletion.src.js | 89 + test-next/test-read-only-properties.html | 18 + test-next/test-read-only-properties.js | 133 ++ test-next/test-read-only-properties.src.js | 104 + test-next/test-reflected-properties.html | 18 + test-next/test-reflected-properties.js | 122 ++ test-next/test-reflected-properties.src.js | 93 + test-next/test-render-root.html | 18 + test-next/test-render-root.js | 38 + test-next/test-render-root.src.js | 39 + test-next/test-render.html | 18 + test-next/test-render.js | 63 + test-next/test-render.src.js | 49 + test-next/test-scratch.html | 18 + test-next/test-scratch.js | 287 +++ test-next/test-scratch.src.js | 286 +++ test-next/test-styles.css | 6 + test-next/test-styles.html | 18 + test-next/test-styles.js | 84 + test-next/test-styles.src.js | 79 + test-next/test-template-engine.html | 20 + test-next/test-template-engine.js | 1149 +++++++++++ test/index.js | 7 +- transpile-decorators.js | 26 + tsconfig.json | 1 + types/x-element-next.d.ts | 261 +++ types/x-element-next.d.ts.map | 1 + x-element-next.js | 1027 ++++++++++ 76 files changed, 16567 insertions(+), 60 deletions(-) create mode 100644 .babelrc.json create mode 100644 DECORATORS.md create mode 100644 test-next/index.html create mode 100644 test-next/index.js create mode 100644 test-next/test-analysis-errors.html create mode 100644 test-next/test-analysis-errors.js create mode 100644 test-next/test-analysis-errors.src.js create mode 100644 test-next/test-attribute-changed-errors.html create mode 100644 test-next/test-attribute-changed-errors.js create mode 100644 test-next/test-attribute-changed-errors.src.js create mode 100644 test-next/test-basic-properties.html create mode 100644 test-next/test-basic-properties.js create mode 100644 test-next/test-basic-properties.src.js create mode 100644 test-next/test-computed-properties.html create mode 100644 test-next/test-computed-properties.js create mode 100644 test-next/test-computed-properties.src.js create mode 100644 test-next/test-default-properties.html create mode 100644 test-next/test-default-properties.js create mode 100644 test-next/test-default-properties.src.js create mode 100644 test-next/test-element-upgrade.html create mode 100644 test-next/test-element-upgrade.js create mode 100644 test-next/test-element-upgrade.src.js create mode 100644 test-next/test-inheritance.html create mode 100644 test-next/test-inheritance.js create mode 100644 test-next/test-inheritance.src.js create mode 100644 test-next/test-initial-properties.html create mode 100644 test-next/test-initial-properties.js create mode 100644 test-next/test-initial-properties.src.js create mode 100644 test-next/test-initialization-errors.html create mode 100644 test-next/test-initialization-errors.js create mode 100644 test-next/test-initialization-errors.src.js create mode 100644 test-next/test-internal-properties.html create mode 100644 test-next/test-internal-properties.js create mode 100644 test-next/test-internal-properties.src.js create mode 100644 test-next/test-listeners.html create mode 100644 test-next/test-listeners.js create mode 100644 test-next/test-listeners.src.js create mode 100644 test-next/test-observed-properties.html create mode 100644 test-next/test-observed-properties.js create mode 100644 test-next/test-observed-properties.src.js create mode 100644 test-next/test-parser.html create mode 100644 test-next/test-parser.js create mode 100644 test-next/test-property-deletion.html create mode 100644 test-next/test-property-deletion.js create mode 100644 test-next/test-property-deletion.src.js create mode 100644 test-next/test-read-only-properties.html create mode 100644 test-next/test-read-only-properties.js create mode 100644 test-next/test-read-only-properties.src.js create mode 100644 test-next/test-reflected-properties.html create mode 100644 test-next/test-reflected-properties.js create mode 100644 test-next/test-reflected-properties.src.js create mode 100644 test-next/test-render-root.html create mode 100644 test-next/test-render-root.js create mode 100644 test-next/test-render-root.src.js create mode 100644 test-next/test-render.html create mode 100644 test-next/test-render.js create mode 100644 test-next/test-render.src.js create mode 100644 test-next/test-scratch.html create mode 100644 test-next/test-scratch.js create mode 100644 test-next/test-scratch.src.js create mode 100644 test-next/test-styles.css create mode 100644 test-next/test-styles.html create mode 100644 test-next/test-styles.js create mode 100644 test-next/test-styles.src.js create mode 100644 test-next/test-template-engine.html create mode 100644 test-next/test-template-engine.js create mode 100644 transpile-decorators.js create mode 100644 types/x-element-next.d.ts create mode 100644 types/x-element-next.d.ts.map create mode 100644 x-element-next.js diff --git a/.babelrc.json b/.babelrc.json new file mode 100644 index 0000000..d6108f0 --- /dev/null +++ b/.babelrc.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + ["@babel/plugin-proposal-decorators", { "version": "2023-05" }] + ] +} diff --git a/CLAUDE.md b/CLAUDE.md index 6a38f40..54d359d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -145,4 +145,5 @@ files should be valid in the latest versions of Chrome, Firefox, and Safari. ## Miscellaneous Development Tips - Use the `--suffix` argument to save things like `--suffix=before` and `--suffix=after` for easier A/B testing. Don't forget to clean up these files after you are finished with all your tasks. -``` \ No newline at end of file +``` +- You can use the `--test-name="pattern"` feature from x-test to target specific tests by the text in the `it()`. \ No newline at end of file diff --git a/DECORATORS.md b/DECORATORS.md new file mode 100644 index 0000000..5a43207 --- /dev/null +++ b/DECORATORS.md @@ -0,0 +1,788 @@ +# Decorator-Based XElement Architecture + +## Overview + +This document describes the decorator-based API for `x-element` using TC39 +Stage 3+ decorators. The implementation is **complete and shipping** - users +can adopt it today with TypeScript 5.0+ or Babel transpilation, and the same +code will work natively when decorators reach Stage 4. + +```javascript +import { XElement, property, listener, html } from '@netflix/x-element'; + +class HelloWorldElement extends XElement { + @property({ type: String, default: 'World' }) + accessor name; + + @property({ type: Number, initial: 0 }) + accessor #count; + + @listener('click') + static #onClick(host) { + host.#count++; + } + + static template(host) { + return html``; + } +} + +customElements.define('hello-world', HelloWorldElement); +``` + +## Key Benefits + +✅ **Idiomatic JavaScript** - Leverages native language features instead of framework-specific patterns +✅ **True privacy** - Private accessors with `#` syntax, native JavaScript encapsulation +✅ **Better IntelliSense** - Decorators analyzed by IDEs, properties visible on prototype +✅ **No invented proxies** - Removes `this.properties` proxy and `this.internal` proxy concepts entirely +✅ **Simpler mental model** - Properties on prototype, callbacks stay static (like current API) +✅ **Simpler configuration** - All config options are simple, single words (removed `readOnly`) +✅ **No breaking changes** - Classic API supported through 2.x +✅ **Future-proof** - Ready for Stage 4 decorators + +## Key Concepts + +- **Constructor** - The class itself (e.g., `MyElement`), where static methods live +- **Prototype** - Where instance members are defined (`MyElement.prototype`) +- **Host** - The actual instance at runtime, passed to callbacks as first parameter +- **Shadow Root** - Shadow DOM render target (bi-directional with host) + +## Core Design Decisions + +1. Properties use auto-accessor syntax: `@property() accessor foo;` +2. Properties live on prototype for IntelliSense and type checking +3. Private properties use native `#` syntax: `@property() accessor #internal;` +4. Initial values via config (`initial`), not accessor initializers +5. No `readOnly` config - use identity transform: `compute: (v) => v` +6. Callbacks remain static method references (like classic API) +7. Arrow function wrappers to avoid TDZ: `(a, b) => MyElement.#method(a, b)` +8. Private static methods by convention (clean public API) +9. Input arrays use strings: `input: ['firstName', 'lastName']` +10. Listener signature: `@listener(type, options?)` +11. Template signature: `static template(host)` with imported `html` +12. Two-file structure: `x-element-next.js` (modern) + `x-element.js` (classic + re-exports) +13. No API mixing - use all-classic or all-modern in inheritance chain +14. Ships today via TypeScript / Babel, native when decorators reach Stage 4 + +## Shipping Before Stage 4 + +**Key insight:** The decorator API infrastructure can be shipped TODAY, even +before decorators reach Stage 4. + +### Why This Works + +The modern `XElement` class and decorator functions (`property()`, `listener()`) +are **just regular JavaScript**: + +```javascript +// x-element.js - Valid JavaScript today! ✅ +const decoratorProperties = new WeakMap(); + +export function property(config) { + return function(target, context) { // Just a function returning a function + decoratorProperties.set(target, config); + return { /* accessor descriptor */ }; + }; +} + +export class XElement extends HTMLElement { + constructor() { + super(); + // Read from WeakMaps - regular JavaScript code + const props = decoratorProperties.get(this.constructor.prototype); + // ... analyze and initialize + } +} +``` + +**Only USER CODE requires decorator support:** + +```javascript +// user-code.js - Requires decorator transpilation ❌ +import { XElement, property } from './x-element.js'; + +class MyElement extends XElement { + @property({ type: String }) // ← Decorator syntax (needs transpilation) + accessor foo; // ← Auto-accessor (needs transpilation) +} +``` + +### Practical Impact + +✅ **Ship immediately** - No need to wait for Stage 4 +✅ **Users adopt now** - With TypeScript 5.0+ or Babel transpilation +✅ **Gather feedback** - Real-world usage informs the API +✅ **Native support ready** - When Stage 4 ships, existing code _just works_ +✅ **No breaking changes** - Same code works with transpilation AND natively + +## Version Isolation + +**Design principle:** We trust developers to use a single x-element version +correctly throughout their application and avoid mixing different versions in +the same inheritance chain. + +### No Enforcement + +Version isolation is **not enforced** by x-element. Detecting and preventing +version mixing across an inheritance chain would require: + +1. Injecting version markers into prototypes +2. Walking the prototype chain during element construction +3. Comparing version identifiers at runtime +4. Handling edge cases like bundler deduplication, npm peer dependencies, + and monorepo scenarios + +This complexity was deemed **not worth the effort** given: + +- Module resolution naturally prevents most version mixing scenarios +- Expert developers understand dependency management +- Any issues that do arise surface as standard JS errors that can be debugged +- The performance cost of runtime checks is undesirable + +### Best Practices + +**Avoid version mixing:** +```javascript +// ✅ GOOD - Single version throughout +import { XElement, property } from 'x-element.js'; + +class Base extends XElement { + @property({ type: String }) + accessor name; +} + +class Derived extends Base { + @property({ type: Number }) + accessor age; +} +``` + +```javascript +// ❌ BAD - Mixing versions (not enforced, but will cause issues) +import { XElement as XElement1 } from 'x-element@1.0.0'; +import { XElement as XElement2 } from 'x-element@2.0.0'; + +class Base extends XElement1 { /* ... */ } +class Derived extends Base { /* ... */ } // Mixed versions in chain +``` + +### API Style Consistency + +Similarly, mixing classic and modern APIs in the same inheritance chain is +**not recommended** but **not enforced**: + +```javascript +// ❌ NOT RECOMMENDED - Mixing API styles +import XElement from 'x-element.js'; // Classic +import { property } from 'x-element.js'; // Modern + +class Base extends XElement { + static get properties() { + return { name: { type: String } }; // Classic API + } +} + +class Derived extends Base { + @property({ type: Number }) // Modern API + accessor age; +} +``` + +Each API style has its own internal metadata system. Mixing them may cause +confusing failures like properties not being defined or observers not running. +We trust developers to maintain consistency within an inheritance chain. + +## Template Signature Change (Proposed) + +**Proposed Change:** Update the template signature to import `html` instead of +receiving it as a parameter. The current `main` branch still passes `html` as +the first argument to `template(html)`, but the decorator-based API will use +imported `html` and a simplified `template(host)` signature. + +### New Signature (Modern) + +```javascript +import { XElement, property, html } from './x-element.js'; + +class MyElement extends XElement { + @property({ type: String }) + accessor name; + + static template(host) { + return html`

Hello ${host.name}!

`; + } +} +``` + +This direct approach: +- Import `html` from x-element +- Framework passes only `host` to `template()` +- Returns template result immediately (no currying) +- Direct property access via `host.name` +- Consistent with observers and listeners + +### Why `template(host)` with Imported `html`? + +Since x-element will restrict to its own template engine, we can import `html` +directly and simplify the template signature to just `template(host)`. + +**Alternative considered: `template(host, html)`** +- Would thread `html` as parameter +- **Rejected:** Since we're restricting to x-element's template engine, no need + to pass `html` as parameter +- **Rejected:** Importing `html` is cleaner and more consistent with other + decorator libraries + +**Why `template(host)` with imported `html` wins:** +- ✅ **Simplest signature** - Just one parameter +- ✅ **No threading** - Import `html` once, use anywhere +- ✅ **Consistency** - Matches patterns from other decorator libraries +- ✅ **Clean imports** - `import { XElement, property, listener, html }` + +**Caching:** For expensive setup, use static class fields instead of the curried pattern: +```javascript +static #dateFormatter = new Intl.DateTimeFormat('en-US'); +static template(host) { + return html`
${this.#dateFormatter.format(host.date)}
`; +} +``` + +## Naming Convention: Private Callbacks + +**Convention:** All compute, observe, and listener callbacks should be +**private static methods** using `#` syntax. This keeps the public API surface +clean—only properties should be public. + +- **Compute methods:** `static #computeFoo()` +- **Observers:** `static #observeFoo()` +- **Listeners:** `static #onClick()` + +The public API surface should consist **only of properties**. Callbacks are +implementation details. + +## Read-Only Properties Pattern + +To create a public read-only property with private write access, use a private property with a public computed property that applies an identity transform: + +```javascript +class MyElement extends XElement { + // Private writable property + @property({ type: String }) + accessor #value; + + // Public read-only via identity transform + @property({ + type: String, + input: ['#value'], + compute: (value) => value, // Pass through unchanged + }) + accessor value; + + @listener('click') + static #onClick(host, event) { + host.#value = 'new'; // ✅ Write to private + console.log(host.value); // ✅ Read from public computed + } +} +``` + +**How it works:** +- Private property (`#value`) is both readable and writable internally +- Public property (`value`) is computed from private property +- Identity transform `(value) => value` simply passes the value through +- Public property automatically updates when private property changes +- Attempting to set public property throws (computed properties are read-only) +- Read from either `host.#value` or `host.value` - choose based on context + +**Benefits:** +- ✅ Uses existing computed property system - no special concepts +- ✅ Explicit and clear - obvious it's a computed value +- ✅ Full property system integration (observers, dependencies work) +- ✅ Simple - just two regular property declarations + +## Method References: Arrow Function Wrappers + +To reference private static methods, use arrow function wrappers to avoid +Temporal Dead Zone (TDZ) errors: + +```javascript +class MyElement extends XElement { + @property({ + input: ['first', 'last'], + compute: (first, last) => MyElement.#computeFull(first, last), + }) + accessor full; + + @property({ + type: String, + observe: (host, value) => MyElement.#observeValue(host, value), + }) + accessor value; + + // Private static methods (implementation details, not public API) + static #computeFull(first, last) { + return `${first} ${last}`; + } + + static #observeValue(host, value) { + console.log('changed', value); + } +} +``` + +This gives us: +- ✅ Type-safe references (no strings!) +- ✅ IntelliSense support +- ✅ Refactor-safe +- ✅ No resolver functions needed +- ✅ Private static methods: `MyElement.#privateMethod` + +### Temporal Dead Zone (TDZ) Workaround + +**Note:** A potential ergonomic fix for this issue is being discussed at +[tc39/proposal-decorators#567](https://github.com/tc39/proposal-decorators/issues/567). + +**Important:** Directly referencing the class name in decorators **will cause +TDZ errors** because decorators run during class construction, before the class +name binding is fully initialized. + +**The solution: Wrap callbacks in arrow functions** to defer evaluation: + +```javascript +class MyElement extends XElement { + @property({ type: String }) + accessor first; + + @property({ type: String }) + accessor last; + + // ❌ WILL cause TDZ error (class reference evaluated immediately during + // decoration) + @property({ + input: ['first', 'last'], + compute: MyElement.#computeFull, // ReferenceError: Cannot access + // 'MyElement' before initialization + }) + accessor full; + + // ✅ Works - arrow function defers class reference until compute is called + @property({ + input: ['first', 'last'], + compute: (first, last) => MyElement.#computeFull(first, last), + }) + accessor full; + + static #computeFull(first, last) { + return `${first} ${last}`; + } +} +``` + +**Why this works:** +- Arrow function **body** is not evaluated until the function is called +- By the time x-element calls the compute function, class construction is + complete +- The class name binding is fully initialized + +**When to use this pattern:** +- ✅ Always safe to use for `compute` and `observe` callbacks +- ✅ Prevents TDZ errors across all environments +- ✅ Allows parameter flexibility - pass only what the method needs + +### Inline Arrow Functions for Simple Callbacks + +**For trivial compute or observe logic, use inline arrow functions:** + +```javascript +class MyElement extends XElement { + @property({ type: String }) + accessor firstName; + + @property({ type: String }) + accessor lastName; + + // Inline arrow function for simple compute + @property({ + input: ['firstName', 'lastName'], + compute: (first, last) => `${first} ${last}`, // ✅ Inline! + }) + accessor fullName; + + // Inline arrow function for simple observe + @property({ + type: Number, + observe: (host, value) => console.log('count:', value), // ✅ Inline! + }) + accessor count; + + // Inline for simple derived value + @property({ + input: ['count'], + compute: (count) => count * 2, // ✅ Inline! + }) + accessor doubled; +} +``` + +**When to use inline vs static methods:** + +**Use inline arrow functions when:** +- ✅ Logic is trivial (one line) +- ✅ No reuse needed +- ✅ Compute is a simple expression +- ✅ Observer just logs or sets simple value + +**Use private static methods when:** +- ✅ Logic is non-trivial (multiple lines) +- ✅ Method might be reused +- ✅ Needs testing separately +- ✅ Complex business logic + +**Example mixing both:** + +```javascript +class MyElement extends XElement { + @property({ + input: ['firstName', 'lastName'], + compute: (first, last) => `${first} ${last}`, // Simple - inline + }) + accessor fullName; + + @property({ + type: String, + // Complex - static method + observe: (host, value) => MyElement.#observeName(host, value), + }) + accessor name; + + static #observeName(host, value) { + // Complex logic with multiple statements + host.#updateCache(value); + host.#notifyListeners(value); + host.#logAnalytics('name_changed', { value }); + } +} +``` + +## Private Fields in Static Methods + +**Key insight:** Static methods can access private instance fields when passed +an instance as a parameter, because private fields are scoped to the class +lexical scope: + +```javascript +class MyElement extends XElement { + @property({ type: String }) + accessor #token; + + @listener('click') + static #onClick(host, event) { + host.#token = 'updated'; // ✅ Static method accesses private field via host + } +} +``` + +JavaScript enforces type safety - passing the wrong instance type throws a +TypeError. + +## DevTools Debugging: Private Field Introspection + +**Critical insight:** Chrome DevTools intentionally allows accessing and +modifying private fields in the console, making the private accessor pattern +practical for real development. + +```javascript +class MyElement extends XElement { + @property({ type: String }) + accessor #token; + + @property({ type: Number, initial: 0 }) + accessor #count; + + @property({ + type: String, + input: ['#token'], + compute: (token) => `public-${token}`, + }) + accessor publicValue; +} +``` + +**In the DevTools console:** +```javascript +// Select element in Elements panel, then in Console: +$0.#token // ✅ Read private field +$0.#token = 'debug-value' // ✅ Write private field +$0.#count // ✅ Inspect private state +$0.#count = 999 // ✅ Modify for testing +``` + +**Why this matters:** + +Without DevTools introspection, private accessors would create a debugging +nightmare - you couldn't inspect or modify internal state during development. +Chrome's intentional design choice to allow console access to private fields +makes this pattern practical: + +- ✅ True encapsulation in production code (private is enforced) +- ✅ Full debugging capability in DevTools (can inspect/modify private state) +- ✅ No special "debug mode" or framework code needed +- ✅ Test edge cases by manipulating private state during development +- ✅ Natural debugging workflow - select element, inspect state, modify values + +**This design wouldn't be viable without DevTools support.** The ability to +introspect private fields is what enables using native JavaScript privacy +features while maintaining an excellent developer experience. + +## Initial Values via Config + +All initial values must be specified in the `@property()` config, not as +accessor initializers. + +```javascript +class MyElement extends XElement { + // Scalar initial values + @property({ type: Number, initial: 0 }) + accessor count; + + @property({ type: String, initial: 'default' }) + accessor name; + + @property({ type: Boolean, initial: false }) + accessor active; + + // Arrays/Objects - MUST use function (to avoid shared references) + @property({ type: Array, initial: () => [] }) + accessor items; + + @property({ type: Object, initial: () => ({ foo: 'bar' }) }) + accessor data; + + // ❌ INVALID - accessor initializers throw error + @property({ type: Number }) + accessor bad = 0; // ❌ Throws: use initial config instead +} +``` + +**Why functions for non-scalars?** +- Scalars (Number, String, Boolean) are copied by value - safe to share +- Arrays/Objects are referenced - would be shared across all instances! +- Functions are called per-instance to create new values + +**Validation:** The decorator's `init()` function throws if any initializer is +provided: +```javascript +init(initialValue) { + if (initialValue !== undefined) { + throw new Error( + `Property "${context.name}" should not have an initializer. ` + + `Use initial config option instead: @property({ initial: ... })` + ); + } + return undefined; +} +``` + +**Benefits:** +- ✅ All configuration in one place +- ✅ Consistent with current x-element behavior +- ✅ TypeScript types properly express `initial?: T | (() => T)` +- ✅ Clear distinction between `initial` and `default` + +**`initial` vs `default`:** +- `initial` - Applied once during element initialization (if value is + nullish) +- `default` - Fallback value when property is set to `undefined` at runtime +```javascript +@property({ type: String, initial: 'initial', default: 'fallback' }) +accessor name; +``` + +## Input Arrays: Why Not Callbacks? + +**Design decision:** Use string arrays (`input: ['firstName', 'lastName']`) +rather than callbacks (`input: (host) => [host.firstName, host.lastName]`). + +A callback approach might provide better IntelliSense (live references to +properties), but was rejected for several reasons: + +**1. Callback interface feels wrong for declarative config:** +```javascript +// ❌ Callback feels imperative, not declarative +@property({ + input: (host) => [host.firstName, host.lastName], + compute: (first, last) => `${first} ${last}`, +}) +accessor fullName; + +// ✅ String array is clearly declarative +@property({ + input: ['firstName', 'lastName'], + compute: (first, last) => `${first} ${last}`, +}) +accessor fullName; +``` + +**2. Dangerous surface area during analysis:** + +The callback provides tempting-but-dangerous access to `host` during class +definition: + +```javascript +// ❌ What if developers do this? +@property({ + input: (host) => { + host.someMethod(); // Called during analysis? 😱 + console.log(host.otherProp); // Side effects during analysis? 😱 + return [host.firstName]; + }, + compute: (first) => `${first}`, +}) +accessor fullName; +``` + +This creates confusion about when the callback executes and encourages misuse. + +**3. Forces analysis timing to first construction:** + +Callback needs a `host` instance, forcing analysis to defer until first +construction (when an instance exists). We may do this anyway, but this design +would force our hand and eliminate analysis-at-definition as an option. + +**4. Requires overly clever initializer tricks:** + +To make IntelliSense work, we'd need to: +- Store a special symbol as the accessor initializer +- Detect this symbol during decorator execution +- This would force us to never accept user initializers, reducing flexibility + +```javascript +// Theoretical implementation would require: +@property({ + input: (host) => [host.firstName], + compute: (first) => first, +}) +accessor fullName = SPECIAL_INTERNALLY_BOUND_SYMBOL; // ← Overly clever + +// And forever prevent this: +accessor fullName = 'default'; // ❌ Would conflict with symbol detection +``` + +**Conclusion:** String arrays are simpler, more declarative, and don't constrain +future implementation choices. The loss of IntelliSense for property names is +acceptable given the philosophy of treating developers as experts who can +reference their own property names. + +## Inheritance and Decorators + +**Derived classes automatically inherit properties and their callbacks.** Base's +static methods can access Base's private fields on derived instances. + +**Key rules:** +- Private callbacks (`#method`) cannot be overridden - use for final components +- Public callbacks (`method`) can be overridden with `super` - use for extensible base classes +- Derived classes cannot access Base's private fields (true encapsulation) +- Use public extension points with private implementation (mixed pattern) for best of both worlds + +## Complete API Example + +```javascript +import { XElement, property, listener, html } from './x-element.js'; + +class MyElement extends XElement { + // Public properties (on prototype) - the only public API surface + @property({ type: String, reflect: true }) + accessor first; + + @property({ type: String }) + accessor last; + + @property({ type: Number, initial: 0 }) + accessor count; + + // Read-only property pattern (private + public computed) + @property({ type: String }) + accessor #status; + + @property({ + type: String, + input: ['#status'], + compute: (status) => status, // Identity transform + }) + accessor status; + + @property({ + input: ['first', 'last'], + // ✅ Private implementation! + compute: (first, last) => MyElement.#computeFull(first, last), + }) + accessor full; + + @property({ + type: String, + // ✅ Private implementation! + observe: (host, value) => MyElement.#observeValue(host, value), + }) + accessor value; + + // Private/internal property (on prototype) + @property({ type: String, initial: 'secret' }) + accessor #internalState; + + // Private static compute method (NO host parameter - pure function) + static #computeFull(first, last) { + return `${first} ${last}`; + } + + // Private static observer method (HAS host parameter) + static #observeValue(host, value) { + console.log(`Value changed to: ${value}`); + host.#internalState = `value-is-${value}`; + } + + // Private event listeners (HAS host parameter) + @listener('click') + static #onClick(host, event) { + host.count++; + host.#status = `Clicked ${host.count} times`; // Write to private + console.log(host.status); // Read from public computed + } + + @listener('keydown', { capture: true }) + static #onKeydown(host, event) { + console.log('key:', event.key); + } + + // Template with imported html + static template(host) { + return html` +

Hello ${host.full}!

+ + `; + } +} +``` + +**Convention: Use private static methods for all callbacks** +- Compute methods: `static #computeFoo()` +- Observers: `static #observeFoo()` +- Listeners: `static #onClick()` +- Only public properties should be on the public API surface + +## Callback Signature Summary + +| Callback Type | Signature | Reason | +|---------------|----------------------------------------------|--------------------------------------| +| **Compute** | `static #computeFoo(...inputs)` | Pure function, memoized, NO host | +| **Observe** | `static #observeFoo(host, value, oldValue)` | Needs host to interact with element | +| **Listener** | `static #onClick(host, event)` | Needs host to interact with element | + +**Note:** All callbacks should be private static methods. They are +implementation details, not public API. + +## Multiple Listeners + +With decorators, you can attach multiple `@listener()` decorators for the same +event type, each with its own method - enabling separation of concerns. +Execution order is top-to-bottom. diff --git a/eslint.config.js b/eslint.config.js index 828ea21..3249b98 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,7 @@ import globals from 'globals'; import jsdoc from 'eslint-plugin-jsdoc'; import NetflixCommon from '@netflix/eslint-config'; +import babelParser from '@babel/eslint-parser'; export default [ jsdoc.configs['flat/recommended'], @@ -11,7 +12,7 @@ export default [ ignores: ['server.js', 'test.js'], }, { - files: ['x-element.js', 'x-parser.js', 'x-template.js'], + files: ['x-element.js', 'x-element-next.js', 'x-parser.js', 'x-template.js'], plugins: { jsdoc }, rules: { 'jsdoc/require-param-description': 'off', @@ -24,11 +25,22 @@ export default [ files: ['performance.js', 'server.js', 'test.js'], languageOptions: { globals: { ...globals.browser, ...globals.node } }, }, + { + files: ['**/*.src.js'], + languageOptions: { + parser: babelParser, + globals: globals.browser, + }, + }, { ignores: [ 'node_modules', - 'demo/react/index.js', // generated from .jsx file - 'performance/react/index.js', // generated from .jsx file + 'demo/react/index.js', // generated from .jsx file + 'performance/react/index.js', // generated from .jsx file + 'test-next/*.js', // transpiled decorator tests (test-next directory) + '!test-next/*.src.js', // but DO lint decorator source files + 'test/test-decorators.js', // transpiled decorator tests (for old test suite) + 'transpile-decorators.js', // build script '*.d.ts', ], }, @@ -38,6 +50,8 @@ export default [ preferredTypes: [ // TypeScript knows about this, but eslint does not. 'TemplateStringsArray', + 'AddEventListenerOptions', + 'CustomElementConstructor', ], }, }, diff --git a/package-lock.json b/package-lock.json index a7e6c7f..2291c32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,10 @@ "version": "2.0.0-rc.14", "license": "Apache-2.0", "devDependencies": { + "@babel/cli": "^7.28.3", + "@babel/core": "^7.28.5", + "@babel/eslint-parser": "^7.28.5", + "@babel/plugin-proposal-decorators": "^7.28.0", "@netflix/eslint-config": "3.0.0", "@netflix/x-test": "2.0.0-rc.2", "@netflix/x-test-cli": "1.0.0-rc.2", @@ -33,120 +37,491 @@ "node": ">=0.10.0" } }, + "node_modules/@babel/cli": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.3.tgz", + "integrity": "sha512-n1RU5vuCX0CsaqaXm9I0KUCNKNQMy5epmzl/xdSSm70bSqhg9GWhgeosypyQLc0bK24+Xpk1WGzZlI9pJtkZdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.28", + "commander": "^6.2.0", + "convert-source-map": "^2.0.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.2.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.6.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz", + "integrity": "sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" }, "engines": { - "node": ">=4" + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", + "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.5", + "semver": "^6.3.1" + }, "engines": { - "node": ">=0.8.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz", + "integrity": "sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@es-joy/jsdoccomment": { @@ -813,6 +1188,56 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@netflix/eslint-config": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@netflix/eslint-config/-/eslint-config-3.0.0.tgz", @@ -851,6 +1276,48 @@ "npm": ">=10.8" } }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@puppeteer/browsers": { "version": "2.10.12", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.12.tgz", @@ -1026,6 +1493,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -1175,6 +1657,16 @@ "bare-path": "^3.0.0" } }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.30.tgz", + "integrity": "sha512-aTUKW4ptQhS64+v2d6IkPzymEzzhw+G0bA1g3uBRV3+ntkH+svttKseW5IOR4Ed6NUVKqnY7qT3dKvzQ7io4AA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/basic-ftp": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", @@ -1185,6 +1677,20 @@ "node": ">=10.0.0" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1196,6 +1702,54 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -1216,6 +1770,27 @@ "node": ">=6" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001756", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", + "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -1233,6 +1808,46 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chromium-bidi": { "version": "10.5.1", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-10.5.1.tgz", @@ -1282,6 +1897,16 @@ "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/comment-parser": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", @@ -1299,6 +1924,13 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -1398,6 +2030,13 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/electron-to-chromium": { + "version": "1.5.259", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz", + "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==", + "dev": true, + "license": "ISC" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1849,6 +2488,20 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1885,6 +2538,45 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1926,6 +2618,28 @@ "node": ">= 14" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2057,6 +2771,25 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", @@ -2074,6 +2807,20 @@ "dev": true, "license": "MIT" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2107,6 +2854,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2144,6 +2902,19 @@ "node": ">=20.0.0" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -2171,6 +2942,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2234,6 +3018,30 @@ "node": ">=12" } }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2277,6 +3085,24 @@ "node": ">= 0.4.0" } }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-deep-merge": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz", @@ -2437,6 +3263,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -2454,6 +3290,37 @@ "dev": true, "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2563,6 +3430,20 @@ "node": ">=18" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2632,6 +3513,16 @@ "node": ">=8" } }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -2841,6 +3732,20 @@ "b4a": "^1.6.4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/to-valid-identifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", @@ -2907,6 +3812,37 @@ "license": "MIT", "optional": true }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2997,6 +3933,13 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, "node_modules/yaml": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", diff --git a/package.json b/package.json index 2a05074..4915426 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "build-react": "npm run build-react-demo && npm run build-react-performance", "build-react-demo": "esbuild demo/react/index.jsx --bundle --format=esm --jsx=automatic --packages=external --external:../* --outfile=demo/react/index.js", "build-react-performance": "esbuild performance/react/index.jsx --bundle --format=esm --jsx=automatic --packages=external --external:../* --outfile=performance/react/index.js", + "transpile-decorators": "node transpile-decorators.js", + "pretest": "npm run transpile-decorators", "test": "x-test --client=puppeteer --url=http://127.0.0.1:8080/test/ --coverage=true", "type": "tsc", "performance": "node ./performance.js", @@ -48,6 +50,10 @@ "/types" ], "devDependencies": { + "@babel/cli": "^7.28.3", + "@babel/core": "^7.28.5", + "@babel/eslint-parser": "^7.28.5", + "@babel/plugin-proposal-decorators": "^7.28.0", "@netflix/eslint-config": "3.0.0", "@netflix/x-test": "2.0.0-rc.2", "@netflix/x-test-cli": "1.0.0-rc.2", diff --git a/test-next/index.html b/test-next/index.html new file mode 100644 index 0000000..83e6013 --- /dev/null +++ b/test-next/index.html @@ -0,0 +1,18 @@ + + + + + + + + +

XElement Tests

+ + + diff --git a/test-next/index.js b/test-next/index.js new file mode 100644 index 0000000..18f82fe --- /dev/null +++ b/test-next/index.js @@ -0,0 +1,23 @@ +import { test } from '@netflix/x-test/x-test.js'; + +test('./test-parser.html'); +test('./test-template-engine.html'); +test('./test-analysis-errors.html'); +test('./test-initialization-errors.html'); +test('./test-attribute-changed-errors.html'); +test('./test-element-upgrade.html'); +test('./test-render.html'); +test('./test-render-root.html'); +test('./test-styles.html'); +test('./test-basic-properties.html'); +test('./test-initial-properties.html'); +test('./test-default-properties.html'); +test('./test-read-only-properties.html'); +test('./test-internal-properties.html'); +test('./test-reflected-properties.html'); +test('./test-computed-properties.html'); +test('./test-observed-properties.html'); +test('./test-listeners.html'); +test('./test-inheritance.html'); +test('./test-property-deletion.html'); +test('./test-scratch.html'); diff --git a/test-next/test-analysis-errors.html b/test-next/test-analysis-errors.html new file mode 100644 index 0000000..94203a2 --- /dev/null +++ b/test-next/test-analysis-errors.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Analysis Errors

+ + + diff --git a/test-next/test-analysis-errors.js b/test-next/test-analysis-errors.js new file mode 100644 index 0000000..fd74dce --- /dev/null +++ b/test-next/test-analysis-errors.js @@ -0,0 +1,1031 @@ +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener } from '../x-element-next.js'; +it('properties should not have hyphens (conflicts with attribute names)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto, _computedKeyDecs, _init_computedKey; + class TestElement extends XElement { + static { + [_init_computedKey, _initProto] = _applyDecs(this, [[_computedKeyDecs, 1, 'just-stop']], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_computedKey(this)); + get [(_computedKeyDecs = property({ + type: String + }), 'just-stop')]() { + return this.#A; + } + set 'just-stop'(v) { + this.#A = v; + } + } + customElements.define('test-element-hyphen', TestElement); + } catch (error) { + // Note: With decorators, the error message may be different as we're + // decorating a quoted property name which might fail at the decorator level + passed = true; + message = error.message; + } + assert(passed, message); +}); +it('property attributes should not have non-standard casing', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto2, _okDecs, _init_ok; + class TestElement extends XElement { + static { + [_init_ok, _initProto2] = _applyDecs(this, [[_okDecs, 1, "ok"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_ok(this)); + get [(_okDecs = property({ + type: String, + attribute: 'this-IS-not-OK' + }), "ok")]() { + return this.#A; + } + set ok(v) { + this.#A = v; + } + } + customElements.define('test-element-attr-case', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.ok has non-standard attribute casing "this-IS-not-OK" (use lower-cased names).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('properties should not shadow XElement prototype interface', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto3, _dispatchErrorDecs, _init_dispatchError; + class TestElement extends XElement { + static { + [_init_dispatchError, _initProto3] = _applyDecs(this, [[_dispatchErrorDecs, 1, "dispatchError"]], [], 0, void 0, XElement).e; + } + #A = (_initProto3(this), _init_dispatchError(this)); + get [(_dispatchErrorDecs = property({}), "dispatchError")]() { + return this.#A; + } + set dispatchError(v) { + this.#A = v; + } + } + customElements.define('test-element-shadow', TestElement); + } catch (error) { + const expected = 'Unexpected key "TestElement.prototype.dispatchError" shadows in XElement.prototype interface.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('property keys should only be from our known set', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto4, _badPropertyDecs, _init_badProperty; + class TestElement extends XElement { + static { + [_init_badProperty, _initProto4] = _applyDecs(this, [[_badPropertyDecs, 1, "badProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto4(this), _init_badProperty(this)); + get [(_badPropertyDecs = property({ + doesNotExist: true + }), "badProperty")]() { + return this.#A; + } + set badProperty(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-key', TestElement); + } catch (error) { + const expected = 'Unexpected key "TestElement.prototype.badProperty.doesNotExist".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('properties should be objects', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto5, _badPropertyDecs2, _init_badProperty2; + class TestElement extends XElement { + static { + [_init_badProperty2, _initProto5] = _applyDecs(this, [[_badPropertyDecs2, 1, "badProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto5(this), _init_badProperty2(this)); + get [(_badPropertyDecs2 = property(undefined), "badProperty")]() { + return this.#A; + } + set badProperty(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-prop-type', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.badProperty has an unexpected value (expected Object, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('type should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto6, _badTypeDecs, _init_badType; + class TestElement extends XElement { + static { + [_init_badType, _initProto6] = _applyDecs(this, [[_badTypeDecs, 1, "badType"]], [], 0, void 0, XElement).e; + } + #A = (_initProto6(this), _init_badType(this)); + get [(_badTypeDecs = property({ + type: undefined + }), "badType")]() { + return this.#A; + } + set badType(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-type', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badType.type" (expected constructor Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('compute should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto7, _badComputeDecs, _init_badCompute; + class TestElement extends XElement { + static { + [_init_badCompute, _initProto7] = _applyDecs(this, [[_badComputeDecs, 1, "badCompute"]], [], 0, void 0, XElement).e; + } + #A = (_initProto7(this), _init_badCompute(this)); + get [(_badComputeDecs = property({ + compute: undefined + }), "badCompute")]() { + return this.#A; + } + set badCompute(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-compute', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badCompute.compute" (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('observe should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto8, _badObserveDecs, _init_badObserve; + class TestElement extends XElement { + static { + [_init_badObserve, _initProto8] = _applyDecs(this, [[_badObserveDecs, 1, "badObserve"]], [], 0, void 0, XElement).e; + } + #A = (_initProto8(this), _init_badObserve(this)); + get [(_badObserveDecs = property({ + observe: undefined + }), "badObserve")]() { + return this.#A; + } + set badObserve(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-observe', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badObserve.observe" (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('attribute should be a string', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto9, _badAttributeDecs, _init_badAttribute; + class TestElement extends XElement { + static { + [_init_badAttribute, _initProto9] = _applyDecs(this, [[_badAttributeDecs, 1, "badAttribute"]], [], 0, void 0, XElement).e; + } + #A = (_initProto9(this), _init_badAttribute(this)); + get [(_badAttributeDecs = property({ + attribute: undefined + }), "badAttribute")]() { + return this.#A; + } + set badAttribute(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-attr', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badAttribute.attribute" (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('attribute should be a non-empty string', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto0, _badAttributeDecs2, _init_badAttribute2; + class TestElement extends XElement { + static { + [_init_badAttribute2, _initProto0] = _applyDecs(this, [[_badAttributeDecs2, 1, "badAttribute"]], [], 0, void 0, XElement).e; + } + #A = (_initProto0(this), _init_badAttribute2(this)); + get [(_badAttributeDecs2 = property({ + attribute: '' + }), "badAttribute")]() { + return this.#A; + } + set badAttribute(v) { + this.#A = v; + } + } + customElements.define('test-element-empty-attr', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badAttribute.attribute" (expected non-empty String).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('attributes cannot be duplicated (1)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto1, _attributeDecs, _init_attribute, _aliasedDecs, _init_aliased; + class TestElement extends XElement { + static { + [_init_attribute, _init_aliased, _initProto1] = _applyDecs(this, [[_attributeDecs, 1, "attribute"], [_aliasedDecs, 1, "aliased"]], [], 0, void 0, XElement).e; + } + #A = (_initProto1(this), _init_attribute(this)); + get [(_attributeDecs = property({ + type: String + }), _aliasedDecs = property({ + type: String, + attribute: 'attribute' + }), "attribute")]() { + return this.#A; + } + set attribute(v) { + this.#A = v; + } + #B = _init_aliased(this); + get aliased() { + return this.#B; + } + set aliased(v) { + this.#B = v; + } + } + customElements.define('test-element-dup-attr-1', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.aliased causes a duplicated attribute "attribute".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('attributes cannot be duplicated (2)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto10, _attributeDecs2, _init_attribute2, _aliasedDecs2, _init_aliased2; + class TestElement extends XElement { + static { + [_init_attribute2, _init_aliased2, _initProto10] = _applyDecs(this, [[_attributeDecs2, 1, "attribute"], [_aliasedDecs2, 1, "aliased"]], [], 0, void 0, XElement).e; + } + #A = (_initProto10(this), _init_attribute2(this)); + get [(_attributeDecs2 = property({}), _aliasedDecs2 = property({ + attribute: 'attribute' + }), "attribute")]() { + return this.#A; + } + set attribute(v) { + this.#A = v; + } + #B = _init_aliased2(this); + get aliased() { + return this.#B; + } + set aliased(v) { + this.#B = v; + } + } + customElements.define('test-element-dup-attr-2', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.aliased causes a duplicated attribute "attribute".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('default must be a scalar or a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto11, _badValueDecs, _init_badValue; + class TestElement extends XElement { + static { + [_init_badValue, _initProto11] = _applyDecs(this, [[_badValueDecs, 1, "badValue"]], [], 0, void 0, XElement).e; + } + #A = (_initProto11(this), _init_badValue(this)); + get [(_badValueDecs = property({ + default: {} + }), "badValue")]() { + return this.#A; + } + set badValue(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-default', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badValue.default" (expected Boolean, String, Number, or Function, got Object).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('reflect should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto12, _badReflectDecs, _init_badReflect; + class TestElement extends XElement { + static { + [_init_badReflect, _initProto12] = _applyDecs(this, [[_badReflectDecs, 1, "badReflect"]], [], 0, void 0, XElement).e; + } + #A = (_initProto12(this), _init_badReflect(this)); + get [(_badReflectDecs = property({ + reflect: undefined + }), "badReflect")]() { + return this.#A; + } + set badReflect(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-reflect', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badReflect.reflect" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The internal configuration doesn't exist in the decorator API. +// Users should use private fields to achieve internal property behavior. +// Skip this test as internal configuration is not supported with decorators. +it.skip('internal should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto13, _badInternalDecs, _init_badInternal; + class TestElement extends XElement { + static { + [_init_badInternal, _initProto13] = _applyDecs(this, [[_badInternalDecs, 1, "badInternal"]], [], 0, void 0, XElement).e; + } + #A = (_initProto13(this), _init_badInternal(this)); + get [(_badInternalDecs = property({ + internal: undefined + }), "badInternal")]() { + return this.#A; + } + set badInternal(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-internal', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInternal.internal" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Instead, users should use accessor-only decorators (no setter) or private +// fields with a getter accessor to achieve read-only behavior. Skip this test +// as readOnly configuration is not supported with decorators. +it.skip('readOnly should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { + badReadOnly: { + readOnly: undefined + } + }; + } + } + customElements.define('test-element-bad-readonly', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badReadOnly.readOnly" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input should be an array', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto14, _badInputDecs, _init_badInput; + class TestElement extends XElement { + static { + [_init_badInput, _initProto14] = _applyDecs(this, [[_badInputDecs, 1, "badInput"]], [], 0, void 0, XElement).e; + } + #A = (_initProto14(this), _init_badInput(this)); + get [(_badInputDecs = property({ + input: {} + }), "badInput")]() { + return this.#A; + } + set badInput(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-input-type', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInput.input" (expected Array, got Object).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input items should be strings', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto15, _badInputDecs2, _init_badInput2; + class TestElement extends XElement { + static { + [_init_badInput2, _initProto15] = _applyDecs(this, [[_badInputDecs2, 1, "badInput"]], [], 0, void 0, XElement).e; + } + #A = (_initProto15(this), _init_badInput2(this)); + get [(_badInputDecs2 = property({ + input: ['foo', 'bar', undefined] + }), "badInput")]() { + return this.#A; + } + set badInput(v) { + this.#A = v; + } + } + customElements.define('test-element-bad-input-items', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInput.input[2]" (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input keys cannot be duplicated', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto16, _fooDecs, _init_foo, _computedDecs, _init_computed; + class TestElement extends XElement { + static { + [_init_foo, _init_computed, _initProto16] = _applyDecs(this, [[_fooDecs, 1, "foo"], [_computedDecs, 1, "computed"]], [], 0, void 0, XElement).e; + } + #A = (_initProto16(this), _init_foo(this)); + get [(_fooDecs = property({ + type: String + }), _computedDecs = property({ + input: ['foo', 'foo'], + compute: () => {} + }), "foo")]() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + #B = _init_computed(this); + get computed() { + return this.#B; + } + set computed(v) { + this.#B = v; + } + } + customElements.define('test-element-duplicate-input', TestElement); + } catch (error) { + const expected = 'Duplicate key "foo" in "TestElement.prototype.computed.input" (each input key must be unique).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input must be declared as other property names', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto17, _fooDecs2, _init_foo2, _badInputDecs3, _init_badInput3; + class TestElement extends XElement { + static { + [_init_foo2, _init_badInput3, _initProto17] = _applyDecs(this, [[_fooDecs2, 1, "foo"], [_badInputDecs3, 1, "badInput"]], [], 0, void 0, XElement).e; + } + #A = (_initProto17(this), _init_foo2(this)); + get [(_fooDecs2 = property({}), _badInputDecs3 = property({ + input: ['foo', 'bar'], + compute: () => {} + }), "foo")]() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + #B = _init_badInput3(this); + get badInput() { + return this.#B; + } + set badInput(v) { + this.#B = v; + } + } + customElements.define('test-element-undeclared-input', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.badInput.input[1] has an unexpected item ("bar" cannot be resolved).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input cannot be cyclic (simple)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto18, _simpleCycleDecs, _init_simpleCycle; + class TestElement extends XElement { + static { + [_init_simpleCycle, _initProto18] = _applyDecs(this, [[_simpleCycleDecs, 1, "simpleCycle"]], [], 0, void 0, XElement).e; + } + #A = (_initProto18(this), _init_simpleCycle(this)); + get [(_simpleCycleDecs = property({ + input: ['simpleCycle'], + compute: () => {} + }), "simpleCycle")]() { + return this.#A; + } + set simpleCycle(v) { + this.#A = v; + } + } + customElements.define('test-element-simple-cycle', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.simpleCycle.input is cyclic.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input cannot be cyclic (complex)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto19, _aDecs, _init_a, _bDecs, _init_b, _cDecs, _init_c; + class TestElement extends XElement { + static { + [_init_a, _init_b, _init_c, _initProto19] = _applyDecs(this, [[_aDecs, 1, "a"], [_bDecs, 1, "b"], [_cDecs, 1, "c"]], [], 0, void 0, XElement).e; + } + #A = (_initProto19(this), _init_a(this)); + get [(_aDecs = property({ + input: ['c'], + compute: () => {} + }), _bDecs = property({ + input: ['a'], + compute: () => {} + }), _cDecs = property({ + input: ['b'], + compute: () => {} + }), "a")]() { + return this.#A; + } + set a(v) { + this.#A = v; + } + #B = _init_b(this); + get b() { + return this.#B; + } + set b(v) { + this.#B = v; + } + #C = _init_c(this); + get c() { + return this.#C; + } + set c(v) { + this.#C = v; + } + } + customElements.define('test-element-complex-cycle', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.a.input is cyclic.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('attribute cannot be declared on unserializable types', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto20, _unserializableDecs, _init_unserializable; + class TestElement extends XElement { + static { + [_init_unserializable, _initProto20] = _applyDecs(this, [[_unserializableDecs, 1, "unserializable"]], [], 0, void 0, XElement).e; + } + #A = (_initProto20(this), _init_unserializable(this)); + get [(_unserializableDecs = property({ + type: Function, + attribute: 'nope' + }), "unserializable")]() { + return this.#A; + } + set unserializable(v) { + this.#A = v; + } + } + customElements.define('test-element-unserializable', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.unserializable.type" (Function) but "TestElement.prototype.unserializable.attribute" is defined.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('input cannot be declared without a compute callback', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto21, _missingComputeDecs, _init_missingCompute; + class TestElement extends XElement { + static { + [_init_missingCompute, _initProto21] = _applyDecs(this, [[_missingComputeDecs, 1, "missingCompute"]], [], 0, void 0, XElement).e; + } + #A = (_initProto21(this), _init_missingCompute(this)); + get [(_missingComputeDecs = property({ + input: ['foo', 'bar', 'baz'] + }), "missingCompute")]() { + return this.#A; + } + set missingCompute(v) { + this.#A = v; + } + } + customElements.define('test-element-missing-compute', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.missingCompute.input" without "TestElement.prototype.missingCompute.compute" (computed properties require a compute callback).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('compute cannot be declared without an input callback', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto22, _missingInputDecs, _init_missingInput; + class TestElement extends XElement { + static { + [_init_missingInput, _initProto22] = _applyDecs(this, [[_missingInputDecs, 1, "missingInput"]], [], 0, void 0, XElement).e; + } + #A = (_initProto22(this), _init_missingInput(this)); + get [(_missingInputDecs = property({ + compute: () => {} + }), "missingInput")]() { + return this.#A; + } + set missingInput(v) { + this.#A = v; + } + } + customElements.define('test-element-missing-input', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.missingInput.compute" without "TestElement.prototype.missingInput.input" (computed properties require input).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('initial cannot be declared for a computed property', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto23, _unexpectedValueDecs, _init_unexpectedValue; + class TestElement extends XElement { + static { + [_init_unexpectedValue, _initProto23] = _applyDecs(this, [[_unexpectedValueDecs, 1, "unexpectedValue"]], [], 0, void 0, XElement).e; + } + #A = (_initProto23(this), _init_unexpectedValue(this)); + get [(_unexpectedValueDecs = property({ + compute: () => {}, + input: [], + initial: 5 + }), "unexpectedValue")]() { + return this.#A; + } + set unexpectedValue(v) { + this.#A = v; + } + } + customElements.define('test-element-computed-initial', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.unexpectedValue.initial" and "TestElement.prototype.unexpectedValue.compute" (computed properties cannot set an initial value).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// This test validates that computed properties cannot be readOnly, but since +// readOnly doesn't exist in decorators, skip this test. +it.skip('readOnly cannot be declared for a computed property', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { + unexpectedValue: { + compute: () => {}, + input: [], + readOnly: true + } + }; + } + } + customElements.define('test-element-computed-readonly', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.unexpectedValue.readOnly" and "TestElement.prototype.unexpectedValue.compute" (computed properties cannot define read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// This test validates that internal properties cannot be readOnly, but since +// readOnly doesn't exist in decorators, skip this test. +it.skip('internal properties cannot also be readOnly', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { + internalReadOnlyProperty: { + type: String, + internal: true, + readOnly: true + } + }; + } + } + customElements.define('test-element-internal-readonly', TestElement); + } catch (error) { + const expected = 'Both "TestElement.prototype.internalReadOnlyProperty.internal" and "TestElement.prototype.internalReadOnlyProperty.readOnly" are true (read-only properties cannot be internal).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('internal properties cannot also be reflected', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto24, _internalReflectedPropertyDecs, _init_internalReflectedProperty, _get_internalReflectedProperty, _set_internalReflectedProperty; + class TestElement extends XElement { + static [(_internalReflectedPropertyDecs = property({ + type: String, + reflect: true + }), "_")]; + static { + delete this._; + [_init_internalReflectedProperty, _get_internalReflectedProperty, _set_internalReflectedProperty, _initProto24] = _applyDecs(this, [[_internalReflectedPropertyDecs, 1, "internalReflectedProperty", o => o.#A, (o, v) => o.#A = v]], [], 0, _ => #internalReflectedProperty in _, XElement).e; + } + #A = (_initProto24(this), _init_internalReflectedProperty(this)); + set #internalReflectedProperty(v) { + _set_internalReflectedProperty(this, v); + } + get #internalReflectedProperty() { + return _get_internalReflectedProperty(this); + } + } + customElements.define('test-element-internal-reflect', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.#internalReflectedProperty.reflect" but property is private (private properties cannot be reflected).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('internal properties cannot define an attribute', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto25, _internalAttributePropertyDecs, _init_internalAttributeProperty, _get_internalAttributeProperty, _set_internalAttributeProperty; + class TestElement extends XElement { + static [(_internalAttributePropertyDecs = property({ + type: String, + attribute: 'custom-attribute' + }), "_")]; + static { + delete this._; + [_init_internalAttributeProperty, _get_internalAttributeProperty, _set_internalAttributeProperty, _initProto25] = _applyDecs(this, [[_internalAttributePropertyDecs, 1, "internalAttributeProperty", o => o.#A, (o, v) => o.#A = v]], [], 0, _ => #internalAttributeProperty in _, XElement).e; + } + #A = (_initProto25(this), _init_internalAttributeProperty(this)); + set #internalAttributeProperty(v) { + _set_internalAttributeProperty(this, v); + } + get #internalAttributeProperty() { + return _get_internalAttributeProperty(this); + } + } + customElements.define('test-element-internal-attr', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.#internalAttributeProperty.attribute" but property is private (private properties cannot have attributes).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('reflected properties must have serializable type', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto26, _nonSerializablePropertyDecs, _init_nonSerializableProperty; + class TestElement extends XElement { + static { + [_init_nonSerializableProperty, _initProto26] = _applyDecs(this, [[_nonSerializablePropertyDecs, 1, "nonSerializableProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto26(this), _init_nonSerializableProperty(this)); + get [(_nonSerializablePropertyDecs = property({ + type: Object, + reflect: true + }), "nonSerializableProperty")]() { + return this.#A; + } + set nonSerializableProperty(v) { + this.#A = v; + } + } + customElements.define('test-element-reflect-nonserialized', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.nonSerializableProperty.type" (Object) but "TestElement.prototype.nonSerializableProperty.reflect" is true.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('reflected properties must have serializable type (2)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto27, _typelessPropertyDecs, _init_typelessProperty; + class TestElement extends XElement { + static { + [_init_typelessProperty, _initProto27] = _applyDecs(this, [[_typelessPropertyDecs, 1, "typelessProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto27(this), _init_typelessProperty(this)); + get [(_typelessPropertyDecs = property({ + reflect: true + }), "typelessProperty")]() { + return this.#A; + } + set typelessProperty(v) { + this.#A = v; + } + } + customElements.define('test-element-reflect-typeless', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.typelessProperty.type" (Undefined) but "TestElement.prototype.typelessProperty.reflect" is true.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('listeners as an object should map to functions', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _fooDecs3, _init_foo3; + class TestElement extends XElement { + static { + [_init_foo3] = _applyDecs(this, [[_fooDecs3, 8, "foo"]], [], 0, void 0, XElement).e; + } + // With decorators, this error would be caught differently since we + // can't decorate non-functions. This test might need to be updated + // to test the decorator API's validation. + static [(_fooDecs3 = listener('click'), "foo")] = _init_foo3(this, undefined); + } + customElements.define('test-element-bad-listener', TestElement); + } catch (error) { + // The error message will be different with decorators + passed = true; + message = error.message; + } + assert(passed, message); +}); +it('listener options should be an object', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initStatic, _onClickDecs; + class TestElement extends XElement { + static { + [_initStatic] = _applyDecs(this, [[_onClickDecs, 10, "onClick"]], [], 0, void 0, XElement).e; + _initStatic(this); + } + static [(_onClickDecs = listener('click', 'not-an-object'), "onClick")]() {} + } + customElements.define('test-element-bad-options', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.onClick" options (expected Object, got String).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: This is a temporary limitation of the babel decorator +// transpiler. We will be able to invert this test later to prove that we can +// in fact use the same private property names in parent and child. +it('cannot use same private property name in parent and child', () => { + let passed = false; + let message = 'no error was thrown'; + try { + let _initProto28, _fieldDecs, _init_field, _get_field, _set_field, _initProto29, _fieldDecs2, _init_field2, _get_field2, _set_field2; + class ParentElement extends XElement { + static [(_fieldDecs = property({ + type: String + }), "_")]; + static { + delete this._; + [_init_field, _get_field, _set_field, _initProto28] = _applyDecs(this, [[_fieldDecs, 1, "field", o => o.#A, (o, v) => o.#A = v]], [], 0, _ => #field in _, XElement).e; + } + #A = (_initProto28(this), _init_field(this)); + set #field(v) { + _set_field(this, v); + } + get #field() { + return _get_field(this); + } + } + class ChildElement extends ParentElement { + static [(_fieldDecs2 = property({ + type: String + }), "_")]; + static { + delete this._; + [_init_field2, _get_field2, _set_field2, _initProto29] = _applyDecs(this, [[_fieldDecs2, 1, "field", o => o.#A, (o, v) => o.#A = v]], [], 0, _ => #field in _, ParentElement).e; + } + #A = (_initProto29(this), _init_field2(this)); + set #field(v) { + _set_field2(this, v); + } + get #field() { + return _get_field2(this); + } + } + customElements.define('test-private-collision', ChildElement); + } catch (error) { + const expected = 'ChildElement: Cannot use private property "#field" in both parent and child classes.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJsaXN0ZW5lciIsInBhc3NlZCIsIm1lc3NhZ2UiLCJfaW5pdFByb3RvIiwiX2NvbXB1dGVkS2V5RGVjcyIsIl9pbml0X2NvbXB1dGVkS2V5IiwiVGVzdEVsZW1lbnQiLCJfYXBwbHlEZWNzIiwiZSIsIkEiLCJ0eXBlIiwiU3RyaW5nIiwianVzdC1zdG9wIiwidiIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiZXJyb3IiLCJfaW5pdFByb3RvMiIsIl9va0RlY3MiLCJfaW5pdF9vayIsImF0dHJpYnV0ZSIsIm9rIiwiZXhwZWN0ZWQiLCJfaW5pdFByb3RvMyIsIl9kaXNwYXRjaEVycm9yRGVjcyIsIl9pbml0X2Rpc3BhdGNoRXJyb3IiLCJkaXNwYXRjaEVycm9yIiwiX2luaXRQcm90bzQiLCJfYmFkUHJvcGVydHlEZWNzIiwiX2luaXRfYmFkUHJvcGVydHkiLCJkb2VzTm90RXhpc3QiLCJiYWRQcm9wZXJ0eSIsIl9pbml0UHJvdG81IiwiX2JhZFByb3BlcnR5RGVjczIiLCJfaW5pdF9iYWRQcm9wZXJ0eTIiLCJ1bmRlZmluZWQiLCJfaW5pdFByb3RvNiIsIl9iYWRUeXBlRGVjcyIsIl9pbml0X2JhZFR5cGUiLCJiYWRUeXBlIiwiX2luaXRQcm90bzciLCJfYmFkQ29tcHV0ZURlY3MiLCJfaW5pdF9iYWRDb21wdXRlIiwiY29tcHV0ZSIsImJhZENvbXB1dGUiLCJfaW5pdFByb3RvOCIsIl9iYWRPYnNlcnZlRGVjcyIsIl9pbml0X2JhZE9ic2VydmUiLCJvYnNlcnZlIiwiYmFkT2JzZXJ2ZSIsIl9pbml0UHJvdG85IiwiX2JhZEF0dHJpYnV0ZURlY3MiLCJfaW5pdF9iYWRBdHRyaWJ1dGUiLCJiYWRBdHRyaWJ1dGUiLCJfaW5pdFByb3RvMCIsIl9iYWRBdHRyaWJ1dGVEZWNzMiIsIl9pbml0X2JhZEF0dHJpYnV0ZTIiLCJfaW5pdFByb3RvMSIsIl9hdHRyaWJ1dGVEZWNzIiwiX2luaXRfYXR0cmlidXRlIiwiX2FsaWFzZWREZWNzIiwiX2luaXRfYWxpYXNlZCIsIkIiLCJhbGlhc2VkIiwiX2luaXRQcm90bzEwIiwiX2F0dHJpYnV0ZURlY3MyIiwiX2luaXRfYXR0cmlidXRlMiIsIl9hbGlhc2VkRGVjczIiLCJfaW5pdF9hbGlhc2VkMiIsIl9pbml0UHJvdG8xMSIsIl9iYWRWYWx1ZURlY3MiLCJfaW5pdF9iYWRWYWx1ZSIsImRlZmF1bHQiLCJiYWRWYWx1ZSIsIl9pbml0UHJvdG8xMiIsIl9iYWRSZWZsZWN0RGVjcyIsIl9pbml0X2JhZFJlZmxlY3QiLCJyZWZsZWN0IiwiYmFkUmVmbGVjdCIsInNraXAiLCJfaW5pdFByb3RvMTMiLCJfYmFkSW50ZXJuYWxEZWNzIiwiX2luaXRfYmFkSW50ZXJuYWwiLCJpbnRlcm5hbCIsImJhZEludGVybmFsIiwicHJvcGVydGllcyIsImJhZFJlYWRPbmx5IiwicmVhZE9ubHkiLCJfaW5pdFByb3RvMTQiLCJfYmFkSW5wdXREZWNzIiwiX2luaXRfYmFkSW5wdXQiLCJpbnB1dCIsImJhZElucHV0IiwiX2luaXRQcm90bzE1IiwiX2JhZElucHV0RGVjczIiLCJfaW5pdF9iYWRJbnB1dDIiLCJfaW5pdFByb3RvMTYiLCJfZm9vRGVjcyIsIl9pbml0X2ZvbyIsIl9jb21wdXRlZERlY3MiLCJfaW5pdF9jb21wdXRlZCIsImZvbyIsImNvbXB1dGVkIiwiX2luaXRQcm90bzE3IiwiX2Zvb0RlY3MyIiwiX2luaXRfZm9vMiIsIl9iYWRJbnB1dERlY3MzIiwiX2luaXRfYmFkSW5wdXQzIiwiX2luaXRQcm90bzE4IiwiX3NpbXBsZUN5Y2xlRGVjcyIsIl9pbml0X3NpbXBsZUN5Y2xlIiwic2ltcGxlQ3ljbGUiLCJfaW5pdFByb3RvMTkiLCJfYURlY3MiLCJfaW5pdF9hIiwiX2JEZWNzIiwiX2luaXRfYiIsIl9jRGVjcyIsIl9pbml0X2MiLCJhIiwiYiIsIkMiLCJjIiwiX2luaXRQcm90bzIwIiwiX3Vuc2VyaWFsaXphYmxlRGVjcyIsIl9pbml0X3Vuc2VyaWFsaXphYmxlIiwiRnVuY3Rpb24iLCJ1bnNlcmlhbGl6YWJsZSIsIl9pbml0UHJvdG8yMSIsIl9taXNzaW5nQ29tcHV0ZURlY3MiLCJfaW5pdF9taXNzaW5nQ29tcHV0ZSIsIm1pc3NpbmdDb21wdXRlIiwiX2luaXRQcm90bzIyIiwiX21pc3NpbmdJbnB1dERlY3MiLCJfaW5pdF9taXNzaW5nSW5wdXQiLCJtaXNzaW5nSW5wdXQiLCJfaW5pdFByb3RvMjMiLCJfdW5leHBlY3RlZFZhbHVlRGVjcyIsIl9pbml0X3VuZXhwZWN0ZWRWYWx1ZSIsImluaXRpYWwiLCJ1bmV4cGVjdGVkVmFsdWUiLCJpbnRlcm5hbFJlYWRPbmx5UHJvcGVydHkiLCJfaW5pdFByb3RvMjQiLCJfaW50ZXJuYWxSZWZsZWN0ZWRQcm9wZXJ0eURlY3MiLCJfaW5pdF9pbnRlcm5hbFJlZmxlY3RlZFByb3BlcnR5IiwiX2dldF9pbnRlcm5hbFJlZmxlY3RlZFByb3BlcnR5IiwiX3NldF9pbnRlcm5hbFJlZmxlY3RlZFByb3BlcnR5IiwiXyIsIm8iLCJpbnRlcm5hbFJlZmxlY3RlZFByb3BlcnR5IiwiI2ludGVybmFsUmVmbGVjdGVkUHJvcGVydHkiLCJfaW5pdFByb3RvMjUiLCJfaW50ZXJuYWxBdHRyaWJ1dGVQcm9wZXJ0eURlY3MiLCJfaW5pdF9pbnRlcm5hbEF0dHJpYnV0ZVByb3BlcnR5IiwiX2dldF9pbnRlcm5hbEF0dHJpYnV0ZVByb3BlcnR5IiwiX3NldF9pbnRlcm5hbEF0dHJpYnV0ZVByb3BlcnR5IiwiaW50ZXJuYWxBdHRyaWJ1dGVQcm9wZXJ0eSIsIiNpbnRlcm5hbEF0dHJpYnV0ZVByb3BlcnR5IiwiX2luaXRQcm90bzI2IiwiX25vblNlcmlhbGl6YWJsZVByb3BlcnR5RGVjcyIsIl9pbml0X25vblNlcmlhbGl6YWJsZVByb3BlcnR5IiwiT2JqZWN0Iiwibm9uU2VyaWFsaXphYmxlUHJvcGVydHkiLCJfaW5pdFByb3RvMjciLCJfdHlwZWxlc3NQcm9wZXJ0eURlY3MiLCJfaW5pdF90eXBlbGVzc1Byb3BlcnR5IiwidHlwZWxlc3NQcm9wZXJ0eSIsIl9mb29EZWNzMyIsIl9pbml0X2ZvbzMiLCJfaW5pdFN0YXRpYyIsIl9vbkNsaWNrRGVjcyIsIl9pbml0UHJvdG8yOCIsIl9maWVsZERlY3MiLCJfaW5pdF9maWVsZCIsIl9nZXRfZmllbGQiLCJfc2V0X2ZpZWxkIiwiX2luaXRQcm90bzI5IiwiX2ZpZWxkRGVjczIiLCJfaW5pdF9maWVsZDIiLCJfZ2V0X2ZpZWxkMiIsIl9zZXRfZmllbGQyIiwiUGFyZW50RWxlbWVudCIsImZpZWxkIiwiI2ZpZWxkIiwiQ2hpbGRFbGVtZW50Il0sInNvdXJjZXMiOlsidGVzdC1hbmFseXNpcy1lcnJvcnMuc3JjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGFzc2VydCwgaXQgfSBmcm9tICdAbmV0ZmxpeC94LXRlc3QveC10ZXN0LmpzJztcbmltcG9ydCB7IFhFbGVtZW50LCBwcm9wZXJ0eSwgbGlzdGVuZXIgfSBmcm9tICcuLi94LWVsZW1lbnQtbmV4dC5qcyc7XG5cbml0KCdwcm9wZXJ0aWVzIHNob3VsZCBub3QgaGF2ZSBoeXBoZW5zIChjb25mbGljdHMgd2l0aCBhdHRyaWJ1dGUgbmFtZXMpJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nIH0pXG4gICAgICBhY2Nlc3NvciAnanVzdC1zdG9wJztcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtaHlwaGVuJywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIE5vdGU6IFdpdGggZGVjb3JhdG9ycywgdGhlIGVycm9yIG1lc3NhZ2UgbWF5IGJlIGRpZmZlcmVudCBhcyB3ZSdyZVxuICAgIC8vIGRlY29yYXRpbmcgYSBxdW90ZWQgcHJvcGVydHkgbmFtZSB3aGljaCBtaWdodCBmYWlsIGF0IHRoZSBkZWNvcmF0b3IgbGV2ZWxcbiAgICBwYXNzZWQgPSB0cnVlO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdwcm9wZXJ0eSBhdHRyaWJ1dGVzIHNob3VsZCBub3QgaGF2ZSBub24tc3RhbmRhcmQgY2FzaW5nJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBhdHRyaWJ1dGU6ICd0aGlzLUlTLW5vdC1PSycgfSlcbiAgICAgIGFjY2Vzc29yIG9rO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1hdHRyLWNhc2UnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVGVzdEVsZW1lbnQucHJvdG90eXBlLm9rIGhhcyBub24tc3RhbmRhcmQgYXR0cmlidXRlIGNhc2luZyBcInRoaXMtSVMtbm90LU9LXCIgKHVzZSBsb3dlci1jYXNlZCBuYW1lcykuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgncHJvcGVydGllcyBzaG91bGQgbm90IHNoYWRvdyBYRWxlbWVudCBwcm90b3R5cGUgaW50ZXJmYWNlJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHt9KVxuICAgICAgYWNjZXNzb3IgZGlzcGF0Y2hFcnJvcjtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtc2hhZG93JywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQga2V5IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLmRpc3BhdGNoRXJyb3JcIiBzaGFkb3dzIGluIFhFbGVtZW50LnByb3RvdHlwZSBpbnRlcmZhY2UuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgncHJvcGVydHkga2V5cyBzaG91bGQgb25seSBiZSBmcm9tIG91ciBrbm93biBzZXQnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBkb2VzTm90RXhpc3Q6IHRydWUgfSlcbiAgICAgIGFjY2Vzc29yIGJhZFByb3BlcnR5O1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQta2V5JywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQga2V5IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLmJhZFByb3BlcnR5LmRvZXNOb3RFeGlzdFwiLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3Byb3BlcnRpZXMgc2hvdWxkIGJlIG9iamVjdHMnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkodW5kZWZpbmVkKVxuICAgICAgYWNjZXNzb3IgYmFkUHJvcGVydHk7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWJhZC1wcm9wLXR5cGUnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVGVzdEVsZW1lbnQucHJvdG90eXBlLmJhZFByb3BlcnR5IGhhcyBhbiB1bmV4cGVjdGVkIHZhbHVlIChleHBlY3RlZCBPYmplY3QsIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3R5cGUgc2hvdWxkIGJlIGEgZnVuY3Rpb24nLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyB0eXBlOiB1bmRlZmluZWQgfSlcbiAgICAgIGFjY2Vzc29yIGJhZFR5cGU7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWJhZC10eXBlJywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQgdmFsdWUgZm9yIFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLmJhZFR5cGUudHlwZVwiIChleHBlY3RlZCBjb25zdHJ1Y3RvciBGdW5jdGlvbiwgZ290IFVuZGVmaW5lZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnY29tcHV0ZSBzaG91bGQgYmUgYSBmdW5jdGlvbicsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7IGNvbXB1dGU6IHVuZGVmaW5lZCB9KVxuICAgICAgYWNjZXNzb3IgYmFkQ29tcHV0ZTtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtYmFkLWNvbXB1dGUnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCB2YWx1ZSBmb3IgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuYmFkQ29tcHV0ZS5jb21wdXRlXCIgKGV4cGVjdGVkIEZ1bmN0aW9uLCBnb3QgVW5kZWZpbmVkKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdvYnNlcnZlIHNob3VsZCBiZSBhIGZ1bmN0aW9uJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgb2JzZXJ2ZTogdW5kZWZpbmVkIH0pXG4gICAgICBhY2Nlc3NvciBiYWRPYnNlcnZlO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtb2JzZXJ2ZScsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRPYnNlcnZlLm9ic2VydmVcIiAoZXhwZWN0ZWQgRnVuY3Rpb24sIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2F0dHJpYnV0ZSBzaG91bGQgYmUgYSBzdHJpbmcnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBhdHRyaWJ1dGU6IHVuZGVmaW5lZCB9KVxuICAgICAgYWNjZXNzb3IgYmFkQXR0cmlidXRlO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtYXR0cicsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRBdHRyaWJ1dGUuYXR0cmlidXRlXCIgKGV4cGVjdGVkIFN0cmluZywgZ290IFVuZGVmaW5lZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnYXR0cmlidXRlIHNob3VsZCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBhdHRyaWJ1dGU6ICcnIH0pXG4gICAgICBhY2Nlc3NvciBiYWRBdHRyaWJ1dGU7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWVtcHR5LWF0dHInLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCB2YWx1ZSBmb3IgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuYmFkQXR0cmlidXRlLmF0dHJpYnV0ZVwiIChleHBlY3RlZCBub24tZW1wdHkgU3RyaW5nKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdhdHRyaWJ1dGVzIGNhbm5vdCBiZSBkdXBsaWNhdGVkICgxKScsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgICAgYWNjZXNzb3IgYXR0cmlidXRlO1xuXG4gICAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGF0dHJpYnV0ZTogJ2F0dHJpYnV0ZScgfSlcbiAgICAgIGFjY2Vzc29yIGFsaWFzZWQ7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWR1cC1hdHRyLTEnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVGVzdEVsZW1lbnQucHJvdG90eXBlLmFsaWFzZWQgY2F1c2VzIGEgZHVwbGljYXRlZCBhdHRyaWJ1dGUgXCJhdHRyaWJ1dGVcIi4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdhdHRyaWJ1dGVzIGNhbm5vdCBiZSBkdXBsaWNhdGVkICgyKScsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7fSlcbiAgICAgIGFjY2Vzc29yIGF0dHJpYnV0ZTtcblxuICAgICAgQHByb3BlcnR5KHsgYXR0cmlidXRlOiAnYXR0cmlidXRlJyB9KVxuICAgICAgYWNjZXNzb3IgYWxpYXNlZDtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtZHVwLWF0dHItMicsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdUZXN0RWxlbWVudC5wcm90b3R5cGUuYWxpYXNlZCBjYXVzZXMgYSBkdXBsaWNhdGVkIGF0dHJpYnV0ZSBcImF0dHJpYnV0ZVwiLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2RlZmF1bHQgbXVzdCBiZSBhIHNjYWxhciBvciBhIGZ1bmN0aW9uJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgZGVmYXVsdDoge30gfSlcbiAgICAgIGFjY2Vzc29yIGJhZFZhbHVlO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtZGVmYXVsdCcsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRWYWx1ZS5kZWZhdWx0XCIgKGV4cGVjdGVkIEJvb2xlYW4sIFN0cmluZywgTnVtYmVyLCBvciBGdW5jdGlvbiwgZ290IE9iamVjdCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgncmVmbGVjdCBzaG91bGQgYmUgYSBib29sZWFuJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgcmVmbGVjdDogdW5kZWZpbmVkIH0pXG4gICAgICBhY2Nlc3NvciBiYWRSZWZsZWN0O1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtcmVmbGVjdCcsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRSZWZsZWN0LnJlZmxlY3RcIiAoZXhwZWN0ZWQgQm9vbGVhbiwgZ290IFVuZGVmaW5lZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBUaGUgaW50ZXJuYWwgY29uZmlndXJhdGlvbiBkb2Vzbid0IGV4aXN0IGluIHRoZSBkZWNvcmF0b3IgQVBJLlxuLy8gIFVzZXJzIHNob3VsZCB1c2UgcHJpdmF0ZSBmaWVsZHMgdG8gYWNoaWV2ZSBpbnRlcm5hbCBwcm9wZXJ0eSBiZWhhdmlvci5cbi8vICBTa2lwIHRoaXMgdGVzdCBhcyBpbnRlcm5hbCBjb25maWd1cmF0aW9uIGlzIG5vdCBzdXBwb3J0ZWQgd2l0aCBkZWNvcmF0b3JzLlxuaXQuc2tpcCgnaW50ZXJuYWwgc2hvdWxkIGJlIGEgYm9vbGVhbicsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7IGludGVybmFsOiB1bmRlZmluZWQgfSlcbiAgICAgIGFjY2Vzc29yIGJhZEludGVybmFsO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtaW50ZXJuYWwnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCB2YWx1ZSBmb3IgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuYmFkSW50ZXJuYWwuaW50ZXJuYWxcIiAoZXhwZWN0ZWQgQm9vbGVhbiwgZ290IFVuZGVmaW5lZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBUaGUgcmVhZE9ubHkgY29uZmlndXJhdGlvbiBkb2Vzbid0IGV4aXN0IGluIHRoZSBkZWNvcmF0b3IgQVBJLlxuLy8gIEluc3RlYWQsIHVzZXJzIHNob3VsZCB1c2UgYWNjZXNzb3Itb25seSBkZWNvcmF0b3JzIChubyBzZXR0ZXIpIG9yIHByaXZhdGVcbi8vICBmaWVsZHMgd2l0aCBhIGdldHRlciBhY2Nlc3NvciB0byBhY2hpZXZlIHJlYWQtb25seSBiZWhhdmlvci4gU2tpcCB0aGlzIHRlc3Rcbi8vICBhcyByZWFkT25seSBjb25maWd1cmF0aW9uIGlzIG5vdCBzdXBwb3J0ZWQgd2l0aCBkZWNvcmF0b3JzLlxuaXQuc2tpcCgncmVhZE9ubHkgc2hvdWxkIGJlIGEgYm9vbGVhbicsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIHN0YXRpYyBnZXQgcHJvcGVydGllcygpIHtcbiAgICAgICAgcmV0dXJuIHsgYmFkUmVhZE9ubHk6IHsgcmVhZE9ubHk6IHVuZGVmaW5lZCB9IH07XG4gICAgICB9XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWJhZC1yZWFkb25seScsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRSZWFkT25seS5yZWFkT25seVwiIChleHBlY3RlZCBCb29sZWFuLCBnb3QgVW5kZWZpbmVkKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdpbnB1dCBzaG91bGQgYmUgYW4gYXJyYXknLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBpbnB1dDoge30gfSlcbiAgICAgIGFjY2Vzc29yIGJhZElucHV0O1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtaW5wdXQtdHlwZScsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRJbnB1dC5pbnB1dFwiIChleHBlY3RlZCBBcnJheSwgZ290IE9iamVjdCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW5wdXQgaXRlbXMgc2hvdWxkIGJlIHN0cmluZ3MnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBpbnB1dDogWydmb28nLCAnYmFyJywgdW5kZWZpbmVkXSB9KVxuICAgICAgYWNjZXNzb3IgYmFkSW5wdXQ7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWJhZC1pbnB1dC1pdGVtcycsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRJbnB1dC5pbnB1dFsyXVwiIChleHBlY3RlZCBTdHJpbmcsIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2lucHV0IGtleXMgY2Fubm90IGJlIGR1cGxpY2F0ZWQnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICAgIGFjY2Vzc29yIGZvbztcblxuICAgICAgQHByb3BlcnR5KHsgaW5wdXQ6IFsnZm9vJywgJ2ZvbyddLCBjb21wdXRlOiAoKSA9PiB7fSB9KVxuICAgICAgYWNjZXNzb3IgY29tcHV0ZWQ7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWR1cGxpY2F0ZS1pbnB1dCcsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdEdXBsaWNhdGUga2V5IFwiZm9vXCIgaW4gXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuY29tcHV0ZWQuaW5wdXRcIiAoZWFjaCBpbnB1dCBrZXkgbXVzdCBiZSB1bmlxdWUpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2lucHV0IG11c3QgYmUgZGVjbGFyZWQgYXMgb3RoZXIgcHJvcGVydHkgbmFtZXMnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoe30pXG4gICAgICBhY2Nlc3NvciBmb287XG5cbiAgICAgIEBwcm9wZXJ0eSh7IGlucHV0OiBbJ2ZvbycsICdiYXInXSwgY29tcHV0ZTogKCkgPT4ge30gfSlcbiAgICAgIGFjY2Vzc29yIGJhZElucHV0O1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC11bmRlY2xhcmVkLWlucHV0JywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Rlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRJbnB1dC5pbnB1dFsxXSBoYXMgYW4gdW5leHBlY3RlZCBpdGVtIChcImJhclwiIGNhbm5vdCBiZSByZXNvbHZlZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW5wdXQgY2Fubm90IGJlIGN5Y2xpYyAoc2ltcGxlKScsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7IGlucHV0OiBbJ3NpbXBsZUN5Y2xlJ10sIGNvbXB1dGU6ICgpID0+IHt9IH0pXG4gICAgICBhY2Nlc3NvciBzaW1wbGVDeWNsZTtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtc2ltcGxlLWN5Y2xlJywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Rlc3RFbGVtZW50LnByb3RvdHlwZS5zaW1wbGVDeWNsZS5pbnB1dCBpcyBjeWNsaWMuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW5wdXQgY2Fubm90IGJlIGN5Y2xpYyAoY29tcGxleCknLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBpbnB1dDogWydjJ10sIGNvbXB1dGU6ICgpID0+IHt9IH0pXG4gICAgICBhY2Nlc3NvciBhO1xuXG4gICAgICBAcHJvcGVydHkoeyBpbnB1dDogWydhJ10sIGNvbXB1dGU6ICgpID0+IHt9IH0pXG4gICAgICBhY2Nlc3NvciBiO1xuXG4gICAgICBAcHJvcGVydHkoeyBpbnB1dDogWydiJ10sIGNvbXB1dGU6ICgpID0+IHt9IH0pXG4gICAgICBhY2Nlc3NvciBjO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1jb21wbGV4LWN5Y2xlJywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Rlc3RFbGVtZW50LnByb3RvdHlwZS5hLmlucHV0IGlzIGN5Y2xpYy4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdhdHRyaWJ1dGUgY2Fubm90IGJlIGRlY2xhcmVkIG9uIHVuc2VyaWFsaXphYmxlIHR5cGVzJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgdHlwZTogRnVuY3Rpb24sIGF0dHJpYnV0ZTogJ25vcGUnIH0pXG4gICAgICBhY2Nlc3NvciB1bnNlcmlhbGl6YWJsZTtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtdW5zZXJpYWxpemFibGUnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnRm91bmQgdW5zZXJpYWxpemFibGUgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUudW5zZXJpYWxpemFibGUudHlwZVwiIChGdW5jdGlvbikgYnV0IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLnVuc2VyaWFsaXphYmxlLmF0dHJpYnV0ZVwiIGlzIGRlZmluZWQuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW5wdXQgY2Fubm90IGJlIGRlY2xhcmVkIHdpdGhvdXQgYSBjb21wdXRlIGNhbGxiYWNrJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgaW5wdXQ6IFsnZm9vJywgJ2JhcicsICdiYXonXSB9KVxuICAgICAgYWNjZXNzb3IgbWlzc2luZ0NvbXB1dGU7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LW1pc3NpbmctY29tcHV0ZScsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdGb3VuZCBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5taXNzaW5nQ29tcHV0ZS5pbnB1dFwiIHdpdGhvdXQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUubWlzc2luZ0NvbXB1dGUuY29tcHV0ZVwiIChjb21wdXRlZCBwcm9wZXJ0aWVzIHJlcXVpcmUgYSBjb21wdXRlIGNhbGxiYWNrKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdjb21wdXRlIGNhbm5vdCBiZSBkZWNsYXJlZCB3aXRob3V0IGFuIGlucHV0IGNhbGxiYWNrJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQHByb3BlcnR5KHsgY29tcHV0ZTogKCkgPT4ge30gfSlcbiAgICAgIGFjY2Vzc29yIG1pc3NpbmdJbnB1dDtcbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtbWlzc2luZy1pbnB1dCcsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdGb3VuZCBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5taXNzaW5nSW5wdXQuY29tcHV0ZVwiIHdpdGhvdXQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUubWlzc2luZ0lucHV0LmlucHV0XCIgKGNvbXB1dGVkIHByb3BlcnRpZXMgcmVxdWlyZSBpbnB1dCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW5pdGlhbCBjYW5ub3QgYmUgZGVjbGFyZWQgZm9yIGEgY29tcHV0ZWQgcHJvcGVydHknLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyBjb21wdXRlOiAoKSA9PiB7fSwgaW5wdXQ6IFtdLCBpbml0aWFsOiA1IH0pXG4gICAgICBhY2Nlc3NvciB1bmV4cGVjdGVkVmFsdWU7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWNvbXB1dGVkLWluaXRpYWwnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnRm91bmQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUudW5leHBlY3RlZFZhbHVlLmluaXRpYWxcIiBhbmQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUudW5leHBlY3RlZFZhbHVlLmNvbXB1dGVcIiAoY29tcHV0ZWQgcHJvcGVydGllcyBjYW5ub3Qgc2V0IGFuIGluaXRpYWwgdmFsdWUpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogVGhlIHJlYWRPbmx5IGNvbmZpZ3VyYXRpb24gZG9lc24ndCBleGlzdCBpbiB0aGUgZGVjb3JhdG9yIEFQSS5cbi8vICBUaGlzIHRlc3QgdmFsaWRhdGVzIHRoYXQgY29tcHV0ZWQgcHJvcGVydGllcyBjYW5ub3QgYmUgcmVhZE9ubHksIGJ1dCBzaW5jZVxuLy8gIHJlYWRPbmx5IGRvZXNuJ3QgZXhpc3QgaW4gZGVjb3JhdG9ycywgc2tpcCB0aGlzIHRlc3QuXG5pdC5za2lwKCdyZWFkT25seSBjYW5ub3QgYmUgZGVjbGFyZWQgZm9yIGEgY29tcHV0ZWQgcHJvcGVydHknLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBzdGF0aWMgZ2V0IHByb3BlcnRpZXMoKSB7XG4gICAgICAgIHJldHVybiB7IHVuZXhwZWN0ZWRWYWx1ZTogeyBjb21wdXRlOiAoKSA9PiB7fSwgaW5wdXQ6IFtdLCByZWFkT25seTogdHJ1ZSB9IH07XG4gICAgICB9XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWNvbXB1dGVkLXJlYWRvbmx5JywgVGVzdEVsZW1lbnQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ZvdW5kIFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLnVuZXhwZWN0ZWRWYWx1ZS5yZWFkT25seVwiIGFuZCBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS51bmV4cGVjdGVkVmFsdWUuY29tcHV0ZVwiIChjb21wdXRlZCBwcm9wZXJ0aWVzIGNhbm5vdCBkZWZpbmUgcmVhZC1vbmx5KS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFRoZSByZWFkT25seSBjb25maWd1cmF0aW9uIGRvZXNuJ3QgZXhpc3QgaW4gdGhlIGRlY29yYXRvciBBUEkuXG4vLyAgVGhpcyB0ZXN0IHZhbGlkYXRlcyB0aGF0IGludGVybmFsIHByb3BlcnRpZXMgY2Fubm90IGJlIHJlYWRPbmx5LCBidXQgc2luY2Vcbi8vICByZWFkT25seSBkb2Vzbid0IGV4aXN0IGluIGRlY29yYXRvcnMsIHNraXAgdGhpcyB0ZXN0LlxuaXQuc2tpcCgnaW50ZXJuYWwgcHJvcGVydGllcyBjYW5ub3QgYWxzbyBiZSByZWFkT25seScsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIHN0YXRpYyBnZXQgcHJvcGVydGllcygpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBpbnRlcm5hbFJlYWRPbmx5UHJvcGVydHk6IHtcbiAgICAgICAgICAgIHR5cGU6IFN0cmluZyxcbiAgICAgICAgICAgIGludGVybmFsOiB0cnVlLFxuICAgICAgICAgICAgcmVhZE9ubHk6IHRydWUsXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtaW50ZXJuYWwtcmVhZG9ubHknLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnQm90aCBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5pbnRlcm5hbFJlYWRPbmx5UHJvcGVydHkuaW50ZXJuYWxcIiBhbmQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuaW50ZXJuYWxSZWFkT25seVByb3BlcnR5LnJlYWRPbmx5XCIgYXJlIHRydWUgKHJlYWQtb25seSBwcm9wZXJ0aWVzIGNhbm5vdCBiZSBpbnRlcm5hbCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW50ZXJuYWwgcHJvcGVydGllcyBjYW5ub3QgYWxzbyBiZSByZWZsZWN0ZWQnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoe1xuICAgICAgICB0eXBlOiBTdHJpbmcsXG4gICAgICAgIHJlZmxlY3Q6IHRydWUsXG4gICAgICB9KVxuICAgICAgYWNjZXNzb3IgI2ludGVybmFsUmVmbGVjdGVkUHJvcGVydHk7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWludGVybmFsLXJlZmxlY3QnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnRm91bmQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuI2ludGVybmFsUmVmbGVjdGVkUHJvcGVydHkucmVmbGVjdFwiIGJ1dCBwcm9wZXJ0eSBpcyBwcml2YXRlIChwcml2YXRlIHByb3BlcnRpZXMgY2Fubm90IGJlIHJlZmxlY3RlZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnaW50ZXJuYWwgcHJvcGVydGllcyBjYW5ub3QgZGVmaW5lIGFuIGF0dHJpYnV0ZScsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7XG4gICAgICAgIHR5cGU6IFN0cmluZyxcbiAgICAgICAgYXR0cmlidXRlOiAnY3VzdG9tLWF0dHJpYnV0ZScsXG4gICAgICB9KVxuICAgICAgYWNjZXNzb3IgI2ludGVybmFsQXR0cmlidXRlUHJvcGVydHk7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWludGVybmFsLWF0dHInLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnRm91bmQgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUuI2ludGVybmFsQXR0cmlidXRlUHJvcGVydHkuYXR0cmlidXRlXCIgYnV0IHByb3BlcnR5IGlzIHByaXZhdGUgKHByaXZhdGUgcHJvcGVydGllcyBjYW5ub3QgaGF2ZSBhdHRyaWJ1dGVzKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCdyZWZsZWN0ZWQgcHJvcGVydGllcyBtdXN0IGhhdmUgc2VyaWFsaXphYmxlIHR5cGUnLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoe1xuICAgICAgICB0eXBlOiBPYmplY3QsXG4gICAgICAgIHJlZmxlY3Q6IHRydWUsXG4gICAgICB9KVxuICAgICAgYWNjZXNzb3Igbm9uU2VyaWFsaXphYmxlUHJvcGVydHk7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LXJlZmxlY3Qtbm9uc2VyaWFsaXplZCcsIFRlc3RFbGVtZW50KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdGb3VuZCB1bnNlcmlhbGl6YWJsZSBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5ub25TZXJpYWxpemFibGVQcm9wZXJ0eS50eXBlXCIgKE9iamVjdCkgYnV0IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLm5vblNlcmlhbGl6YWJsZVByb3BlcnR5LnJlZmxlY3RcIiBpcyB0cnVlLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3JlZmxlY3RlZCBwcm9wZXJ0aWVzIG11c3QgaGF2ZSBzZXJpYWxpemFibGUgdHlwZSAoMiknLCAoKSA9PiB7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoe1xuICAgICAgICByZWZsZWN0OiB0cnVlLFxuICAgICAgfSlcbiAgICAgIGFjY2Vzc29yIHR5cGVsZXNzUHJvcGVydHk7XG4gICAgfVxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LXJlZmxlY3QtdHlwZWxlc3MnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnRm91bmQgdW5zZXJpYWxpemFibGUgXCJUZXN0RWxlbWVudC5wcm90b3R5cGUudHlwZWxlc3NQcm9wZXJ0eS50eXBlXCIgKFVuZGVmaW5lZCkgYnV0IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLnR5cGVsZXNzUHJvcGVydHkucmVmbGVjdFwiIGlzIHRydWUuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnbGlzdGVuZXJzIGFzIGFuIG9iamVjdCBzaG91bGQgbWFwIHRvIGZ1bmN0aW9ucycsICgpID0+IHtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICAgIC8vIFdpdGggZGVjb3JhdG9ycywgdGhpcyBlcnJvciB3b3VsZCBiZSBjYXVnaHQgZGlmZmVyZW50bHkgc2luY2Ugd2VcbiAgICAgIC8vIGNhbid0IGRlY29yYXRlIG5vbi1mdW5jdGlvbnMuIFRoaXMgdGVzdCBtaWdodCBuZWVkIHRvIGJlIHVwZGF0ZWRcbiAgICAgIC8vIHRvIHRlc3QgdGhlIGRlY29yYXRvciBBUEkncyB2YWxpZGF0aW9uLlxuICAgICAgQGxpc3RlbmVyKCdjbGljaycpXG4gICAgICBzdGF0aWMgZm9vID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYWQtbGlzdGVuZXInLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gVGhlIGVycm9yIG1lc3NhZ2Ugd2lsbCBiZSBkaWZmZXJlbnQgd2l0aCBkZWNvcmF0b3JzXG4gICAgcGFzc2VkID0gdHJ1ZTtcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnbGlzdGVuZXIgb3B0aW9ucyBzaG91bGQgYmUgYW4gb2JqZWN0JywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgICAgQGxpc3RlbmVyKCdjbGljaycsICdub3QtYW4tb2JqZWN0JylcbiAgICAgIHN0YXRpYyBvbkNsaWNrKCkge31cbiAgICB9XG4gICAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtYmFkLW9wdGlvbnMnLCBUZXN0RWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCB2YWx1ZSBmb3IgXCJUZXN0RWxlbWVudC5vbkNsaWNrXCIgb3B0aW9ucyAoZXhwZWN0ZWQgT2JqZWN0LCBnb3QgU3RyaW5nKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFRoaXMgaXMgYSB0ZW1wb3JhcnkgbGltaXRhdGlvbiBvZiB0aGUgYmFiZWwgZGVjb3JhdG9yXG4vLyAgdHJhbnNwaWxlci4gV2Ugd2lsbCBiZSBhYmxlIHRvIGludmVydCB0aGlzIHRlc3QgbGF0ZXIgdG8gcHJvdmUgdGhhdCB3ZSBjYW5cbi8vICBpbiBmYWN0IHVzZSB0aGUgc2FtZSBwcml2YXRlIHByb3BlcnR5IG5hbWVzIGluIHBhcmVudCBhbmQgY2hpbGQuXG5pdCgnY2Fubm90IHVzZSBzYW1lIHByaXZhdGUgcHJvcGVydHkgbmFtZSBpbiBwYXJlbnQgYW5kIGNoaWxkJywgKCkgPT4ge1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGNsYXNzIFBhcmVudEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICAgIGFjY2Vzc29yICNmaWVsZDtcbiAgICB9XG5cbiAgICBjbGFzcyBDaGlsZEVsZW1lbnQgZXh0ZW5kcyBQYXJlbnRFbGVtZW50IHtcbiAgICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgICAgYWNjZXNzb3IgI2ZpZWxkO1xuICAgIH1cblxuICAgIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1wcml2YXRlLWNvbGxpc2lvbicsIENoaWxkRWxlbWVudCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnQ2hpbGRFbGVtZW50OiBDYW5ub3QgdXNlIHByaXZhdGUgcHJvcGVydHkgXCIjZmllbGRcIiBpbiBib3RoIHBhcmVudCBhbmQgY2hpbGQgY2xhc3Nlcy4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsU0FBU0EsTUFBTSxFQUFFQyxFQUFFLFFBQVEsMkJBQTJCO0FBQ3RELFNBQVNDLFFBQVEsRUFBRUMsUUFBUSxFQUFFQyxRQUFRLFFBQVEsc0JBQXNCO0FBRW5FSCxFQUFFLENBQUMscUVBQXFFLEVBQUUsTUFBTTtFQUM5RSxJQUFJSSxNQUFNLEdBQUcsS0FBSztFQUNsQixJQUFJQyxPQUFPLEdBQUcs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LENBQUEsSUFBQUEsQ0FBQSxFQUFBM0ksQ0FBQSxHQUFBMkksQ0FBQSxFQUFBdkksQ0FBQSxLQUFBdUksQ0FBQSxFQUFBM0ksQ0FBQSxHQUFBSSxDQUFBLFdBQUFzSSxDQUFBLElBQ2xCLENBQUNtQyxLQUFLLElBQUFuQyxDQUFBLEVBRldySixRQUFRLEVBQUFVLENBQUE7TUFBQTtNQUFBLENBQUFDLENBQUEsSUFBQWtLLFlBQUEsUUFBQUUsV0FBQTtNQUFBLElBRXpCLENBQUNTLEtBQUtDLENBQUExSyxDQUFBO1FBQUFrSyxVQUFBLE9BQUFsSyxDQUFBO01BQUE7TUFBQSxJQUFOLENBQUN5SyxLQUFLQyxDQUFBO1FBQUEsT0FBQVQsVUFBQTtNQUFBO0lBQ2pCO0lBRUEsTUFBTVUsWUFBWSxTQUFTSCxhQUFhLENBQUM7TUFBQSxTQUFBSixXQUFBLEdBQ3RDbEwsUUFBUSxDQUFDO1FBQUVXLElBQUksRUFBRUM7TUFBTyxDQUFDLENBQUM7TUFBQTtRQUFBLFlBQUF3SSxDQUFBO1FBQUEsQ0FBQStCLFlBQUEsRUFBQUMsV0FBQSxFQUFBQyxXQUFBLEVBQUFKLFlBQUEsSUFBQXpLLFVBQUEsU0FBQTBLLFdBQUEsY0FBQTdCLENBQUEsSUFBQUEsQ0FBQSxFQUFBM0ksQ0FBQSxHQUFBMkksQ0FBQSxFQUFBdkksQ0FBQSxLQUFBdUksQ0FBQSxFQUFBM0ksQ0FBQSxHQUFBSSxDQUFBLFdBQUFzSSxDQUFBLElBQ2xCLENBQUNtQyxLQUFLLElBQUFuQyxDQUFBLEVBRlVrQyxhQUFhLEVBQUE3SyxDQUFBO01BQUE7TUFBQSxDQUFBQyxDQUFBLElBQUF1SyxZQUFBLFFBQUFFLFlBQUE7TUFBQSxJQUU3QixDQUFDSSxLQUFLQyxDQUFBMUssQ0FBQTtRQUFBdUssV0FBQSxPQUFBdkssQ0FBQTtNQUFBO01BQUEsSUFBTixDQUFDeUssS0FBS0MsQ0FBQTtRQUFBLE9BQUFKLFdBQUE7TUFBQTtJQUNqQjtJQUVBckssY0FBYyxDQUFDQyxNQUFNLENBQUMsd0JBQXdCLEVBQUV5SyxZQUFZLENBQUM7RUFDL0QsQ0FBQyxDQUFDLE9BQU94SyxLQUFLLEVBQUU7SUFDZCxNQUFNTSxRQUFRLEdBQUcsc0ZBQXNGO0lBQ3ZHcEIsT0FBTyxHQUFHYyxLQUFLLENBQUNkLE9BQU87SUFDdkJELE1BQU0sR0FBR2UsS0FBSyxDQUFDZCxPQUFPLEtBQUtvQixRQUFRO0VBQ3JDO0VBQ0ExQixNQUFNLENBQUNLLE1BQU0sRUFBRUMsT0FBTyxDQUFDO0FBQ3pCLENBQUMsQ0FBQyIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/test-next/test-analysis-errors.src.js b/test-next/test-analysis-errors.src.js new file mode 100644 index 0000000..678bcb4 --- /dev/null +++ b/test-next/test-analysis-errors.src.js @@ -0,0 +1,661 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener } from '../x-element-next.js'; + +it('properties should not have hyphens (conflicts with attribute names)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: String }) + accessor 'just-stop'; + } + customElements.define('test-element-hyphen', TestElement); + } catch (error) { + // Note: With decorators, the error message may be different as we're + // decorating a quoted property name which might fail at the decorator level + passed = true; + message = error.message; + } + assert(passed, message); +}); + +it('property attributes should not have non-standard casing', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: String, attribute: 'this-IS-not-OK' }) + accessor ok; + } + customElements.define('test-element-attr-case', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.ok has non-standard attribute casing "this-IS-not-OK" (use lower-cased names).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('properties should not shadow XElement prototype interface', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({}) + accessor dispatchError; + } + customElements.define('test-element-shadow', TestElement); + } catch (error) { + const expected = 'Unexpected key "TestElement.prototype.dispatchError" shadows in XElement.prototype interface.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('property keys should only be from our known set', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ doesNotExist: true }) + accessor badProperty; + } + customElements.define('test-element-bad-key', TestElement); + } catch (error) { + const expected = 'Unexpected key "TestElement.prototype.badProperty.doesNotExist".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('properties should be objects', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property(undefined) + accessor badProperty; + } + customElements.define('test-element-bad-prop-type', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.badProperty has an unexpected value (expected Object, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('type should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: undefined }) + accessor badType; + } + customElements.define('test-element-bad-type', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badType.type" (expected constructor Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('compute should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ compute: undefined }) + accessor badCompute; + } + customElements.define('test-element-bad-compute', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badCompute.compute" (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('observe should be a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ observe: undefined }) + accessor badObserve; + } + customElements.define('test-element-bad-observe', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badObserve.observe" (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('attribute should be a string', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ attribute: undefined }) + accessor badAttribute; + } + customElements.define('test-element-bad-attr', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badAttribute.attribute" (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('attribute should be a non-empty string', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ attribute: '' }) + accessor badAttribute; + } + customElements.define('test-element-empty-attr', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badAttribute.attribute" (expected non-empty String).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('attributes cannot be duplicated (1)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: String }) + accessor attribute; + + @property({ type: String, attribute: 'attribute' }) + accessor aliased; + } + customElements.define('test-element-dup-attr-1', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.aliased causes a duplicated attribute "attribute".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('attributes cannot be duplicated (2)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({}) + accessor attribute; + + @property({ attribute: 'attribute' }) + accessor aliased; + } + customElements.define('test-element-dup-attr-2', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.aliased causes a duplicated attribute "attribute".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('default must be a scalar or a function', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ default: {} }) + accessor badValue; + } + customElements.define('test-element-bad-default', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badValue.default" (expected Boolean, String, Number, or Function, got Object).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('reflect should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ reflect: undefined }) + accessor badReflect; + } + customElements.define('test-element-bad-reflect', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badReflect.reflect" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The internal configuration doesn't exist in the decorator API. +// Users should use private fields to achieve internal property behavior. +// Skip this test as internal configuration is not supported with decorators. +it.skip('internal should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ internal: undefined }) + accessor badInternal; + } + customElements.define('test-element-bad-internal', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInternal.internal" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Instead, users should use accessor-only decorators (no setter) or private +// fields with a getter accessor to achieve read-only behavior. Skip this test +// as readOnly configuration is not supported with decorators. +it.skip('readOnly should be a boolean', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { badReadOnly: { readOnly: undefined } }; + } + } + customElements.define('test-element-bad-readonly', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badReadOnly.readOnly" (expected Boolean, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input should be an array', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ input: {} }) + accessor badInput; + } + customElements.define('test-element-bad-input-type', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInput.input" (expected Array, got Object).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input items should be strings', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ input: ['foo', 'bar', undefined] }) + accessor badInput; + } + customElements.define('test-element-bad-input-items', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.badInput.input[2]" (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input keys cannot be duplicated', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: String }) + accessor foo; + + @property({ input: ['foo', 'foo'], compute: () => {} }) + accessor computed; + } + customElements.define('test-element-duplicate-input', TestElement); + } catch (error) { + const expected = 'Duplicate key "foo" in "TestElement.prototype.computed.input" (each input key must be unique).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input must be declared as other property names', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({}) + accessor foo; + + @property({ input: ['foo', 'bar'], compute: () => {} }) + accessor badInput; + } + customElements.define('test-element-undeclared-input', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.badInput.input[1] has an unexpected item ("bar" cannot be resolved).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input cannot be cyclic (simple)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ input: ['simpleCycle'], compute: () => {} }) + accessor simpleCycle; + } + customElements.define('test-element-simple-cycle', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.simpleCycle.input is cyclic.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input cannot be cyclic (complex)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ input: ['c'], compute: () => {} }) + accessor a; + + @property({ input: ['a'], compute: () => {} }) + accessor b; + + @property({ input: ['b'], compute: () => {} }) + accessor c; + } + customElements.define('test-element-complex-cycle', TestElement); + } catch (error) { + const expected = 'TestElement.prototype.a.input is cyclic.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('attribute cannot be declared on unserializable types', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ type: Function, attribute: 'nope' }) + accessor unserializable; + } + customElements.define('test-element-unserializable', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.unserializable.type" (Function) but "TestElement.prototype.unserializable.attribute" is defined.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('input cannot be declared without a compute callback', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ input: ['foo', 'bar', 'baz'] }) + accessor missingCompute; + } + customElements.define('test-element-missing-compute', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.missingCompute.input" without "TestElement.prototype.missingCompute.compute" (computed properties require a compute callback).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('compute cannot be declared without an input callback', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ compute: () => {} }) + accessor missingInput; + } + customElements.define('test-element-missing-input', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.missingInput.compute" without "TestElement.prototype.missingInput.input" (computed properties require input).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('initial cannot be declared for a computed property', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ compute: () => {}, input: [], initial: 5 }) + accessor unexpectedValue; + } + customElements.define('test-element-computed-initial', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.unexpectedValue.initial" and "TestElement.prototype.unexpectedValue.compute" (computed properties cannot set an initial value).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// This test validates that computed properties cannot be readOnly, but since +// readOnly doesn't exist in decorators, skip this test. +it.skip('readOnly cannot be declared for a computed property', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { unexpectedValue: { compute: () => {}, input: [], readOnly: true } }; + } + } + customElements.define('test-element-computed-readonly', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.unexpectedValue.readOnly" and "TestElement.prototype.unexpectedValue.compute" (computed properties cannot define read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// This test validates that internal properties cannot be readOnly, but since +// readOnly doesn't exist in decorators, skip this test. +it.skip('internal properties cannot also be readOnly', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + static get properties() { + return { + internalReadOnlyProperty: { + type: String, + internal: true, + readOnly: true, + }, + }; + } + } + customElements.define('test-element-internal-readonly', TestElement); + } catch (error) { + const expected = 'Both "TestElement.prototype.internalReadOnlyProperty.internal" and "TestElement.prototype.internalReadOnlyProperty.readOnly" are true (read-only properties cannot be internal).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('internal properties cannot also be reflected', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ + type: String, + reflect: true, + }) + accessor #internalReflectedProperty; + } + customElements.define('test-element-internal-reflect', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.#internalReflectedProperty.reflect" but property is private (private properties cannot be reflected).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('internal properties cannot define an attribute', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ + type: String, + attribute: 'custom-attribute', + }) + accessor #internalAttributeProperty; + } + customElements.define('test-element-internal-attr', TestElement); + } catch (error) { + const expected = 'Found "TestElement.prototype.#internalAttributeProperty.attribute" but property is private (private properties cannot have attributes).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('reflected properties must have serializable type', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ + type: Object, + reflect: true, + }) + accessor nonSerializableProperty; + } + customElements.define('test-element-reflect-nonserialized', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.nonSerializableProperty.type" (Object) but "TestElement.prototype.nonSerializableProperty.reflect" is true.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('reflected properties must have serializable type (2)', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @property({ + reflect: true, + }) + accessor typelessProperty; + } + customElements.define('test-element-reflect-typeless', TestElement); + } catch (error) { + const expected = 'Found unserializable "TestElement.prototype.typelessProperty.type" (Undefined) but "TestElement.prototype.typelessProperty.reflect" is true.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('listeners as an object should map to functions', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + // With decorators, this error would be caught differently since we + // can't decorate non-functions. This test might need to be updated + // to test the decorator API's validation. + @listener('click') + static foo = undefined; + } + customElements.define('test-element-bad-listener', TestElement); + } catch (error) { + // The error message will be different with decorators + passed = true; + message = error.message; + } + assert(passed, message); +}); + +it('listener options should be an object', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class TestElement extends XElement { + @listener('click', 'not-an-object') + static onClick() {} + } + customElements.define('test-element-bad-options', TestElement); + } catch (error) { + const expected = 'Unexpected value for "TestElement.onClick" options (expected Object, got String).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: This is a temporary limitation of the babel decorator +// transpiler. We will be able to invert this test later to prove that we can +// in fact use the same private property names in parent and child. +it('cannot use same private property name in parent and child', () => { + let passed = false; + let message = 'no error was thrown'; + try { + class ParentElement extends XElement { + @property({ type: String }) + accessor #field; + } + + class ChildElement extends ParentElement { + @property({ type: String }) + accessor #field; + } + + customElements.define('test-private-collision', ChildElement); + } catch (error) { + const expected = 'ChildElement: Cannot use private property "#field" in both parent and child classes.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-attribute-changed-errors.html b/test-next/test-attribute-changed-errors.html new file mode 100644 index 0000000..4f4e508 --- /dev/null +++ b/test-next/test-attribute-changed-errors.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Attribute Changed Errors

+ + + diff --git a/test-next/test-attribute-changed-errors.js b/test-next/test-attribute-changed-errors.js new file mode 100644 index 0000000..9edbf40 --- /dev/null +++ b/test-next/test-attribute-changed-errors.js @@ -0,0 +1,71 @@ +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property } from '../x-element-next.js'; + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Users should use accessor-only decorators (no setter) or private fields +// to achieve read-only behavior. Skip this test as readOnly is not supported. +it.skip('errors are thrown in attributeChangedCallback for read-only properties', () => { + // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. + class TestElement extends XElement { + static get properties() { + return { + readOnlyProperty: { + type: String, + readOnly: true + } + }; + } + } + customElements.define('test-element-2', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + el.connectedCallback(); + try { + el.attributeChangedCallback('read-only-property', null, 'nope'); + } catch (error) { + const expected = 'Property "TestElement.prototype.readOnlyProperty" is read-only.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('errors are thrown in attributeChangedCallback for computed properties', () => { + let _initProto, _computedDecs, _init_computed; + // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. + class TestElement extends XElement { + static { + [_init_computed, _initProto] = _applyDecs(this, [[_computedDecs, 1, "computed"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_computed(this)); + get [(_computedDecs = property({ + type: String, + input: [], + compute: () => {} + }), "computed")]() { + return this.#A; + } + set computed(v) { + this.#A = v; + } + } + customElements.define('test-element-attr-computed', TestElement); + const el = new TestElement(); + el.connectedCallback(); + let passed = false; + let message = 'no error was thrown'; + try { + el.attributeChangedCallback('computed', null, 'nope'); + } catch (error) { + const expected = 'Property "TestElement.prototype.computed" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJza2lwIiwiVGVzdEVsZW1lbnQiLCJwcm9wZXJ0aWVzIiwicmVhZE9ubHlQcm9wZXJ0eSIsInR5cGUiLCJTdHJpbmciLCJyZWFkT25seSIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiZWwiLCJwYXNzZWQiLCJtZXNzYWdlIiwiY29ubmVjdGVkQ2FsbGJhY2siLCJhdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2siLCJlcnJvciIsImV4cGVjdGVkIiwiX2luaXRQcm90byIsIl9jb21wdXRlZERlY3MiLCJfaW5pdF9jb21wdXRlZCIsIl9hcHBseURlY3MiLCJlIiwiQSIsImlucHV0IiwiY29tcHV0ZSIsImNvbXB1dGVkIiwidiJdLCJzb3VyY2VzIjpbInRlc3QtYXR0cmlidXRlLWNoYW5nZWQtZXJyb3JzLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHkgfSBmcm9tICcuLi94LWVsZW1lbnQtbmV4dC5qcyc7XG5cbi8vIFRPRE86ICMzNDY6IFRoZSByZWFkT25seSBjb25maWd1cmF0aW9uIGRvZXNuJ3QgZXhpc3QgaW4gdGhlIGRlY29yYXRvciBBUEkuXG4vLyAgVXNlcnMgc2hvdWxkIHVzZSBhY2Nlc3Nvci1vbmx5IGRlY29yYXRvcnMgKG5vIHNldHRlcikgb3IgcHJpdmF0ZSBmaWVsZHNcbi8vICB0byBhY2hpZXZlIHJlYWQtb25seSBiZWhhdmlvci4gU2tpcCB0aGlzIHRlc3QgYXMgcmVhZE9ubHkgaXMgbm90IHN1cHBvcnRlZC5cbml0LnNraXAoJ2Vycm9ycyBhcmUgdGhyb3duIGluIGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayBmb3IgcmVhZC1vbmx5IHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIC8vIFdlIGNhbm5vdCB0cnktY2F0Y2ggc2V0QXR0cmlidXRlLCBzbyB3ZSBmYWtlIHRoZSBhdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2suXG4gIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIHN0YXRpYyBnZXQgcHJvcGVydGllcygpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHJlYWRPbmx5UHJvcGVydHk6IHtcbiAgICAgICAgICB0eXBlOiBTdHJpbmcsXG4gICAgICAgICAgcmVhZE9ubHk6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH1cbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC0yJywgVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB0cnkge1xuICAgIGVsLmF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjaygncmVhZC1vbmx5LXByb3BlcnR5JywgbnVsbCwgJ25vcGUnKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5yZWFkT25seVByb3BlcnR5XCIgaXMgcmVhZC1vbmx5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2Vycm9ycyBhcmUgdGhyb3duIGluIGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayBmb3IgY29tcHV0ZWQgcHJvcGVydGllcycsICgpID0+IHtcbiAgLy8gV2UgY2Fubm90IHRyeS1jYXRjaCBzZXRBdHRyaWJ1dGUsIHNvIHdlIGZha2UgdGhlIGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjay5cbiAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHtcbiAgICAgIHR5cGU6IFN0cmluZyxcbiAgICAgIGlucHV0OiBbXSxcbiAgICAgIGNvbXB1dGU6ICgpID0+IHt9LFxuICAgIH0pXG4gICAgYWNjZXNzb3IgY29tcHV0ZWQ7XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtYXR0ci1jb21wdXRlZCcsIFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVzdEVsZW1lbnQoKTtcbiAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5hdHRyaWJ1dGVDaGFuZ2VkQ2FsbGJhY2soJ2NvbXB1dGVkJywgbnVsbCwgJ25vcGUnKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5jb21wdXRlZFwiIGlzIGNvbXB1dGVkIChjb21wdXRlZCBwcm9wZXJ0aWVzIGFyZSByZWFkLW9ubHkpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxTQUFTQSxNQUFNLEVBQUVDLEVBQUUsUUFBUSwyQkFBMkI7QUFDdEQsU0FBU0MsUUFBUSxFQUFFQyxRQUFRLFFBQVEsc0JBQXNCOztBQUV6RDtBQUNBO0FBQ0E7QUFDQUYsRUFBRSxDQUFDRyxJQUFJLENBQUMsd0VBQXdFLEVBQUUsTUFBTTtFQUN0RjtFQUNBLE1BQU1DLFdBQVcsU0FBU0gsUUFBUSxDQUFDO0lBQ2pDLFdBQVdJLFVBQVVBLENBQUEsRUFBRztNQUN0QixPQUFPO1FBQ0xDLGdCQUFnQixFQUFFO1VBQ2hCQyxJQUFJLEVBQUVDLE1BQU07VUFDWkMsUUFBUSxFQUFFO1FBQ1o7TUFDRixDQUFDO0lBQ0g7RUFDRjtFQUNBQyxjQUFjLENBQUNDLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRVAsV0FBVyxDQUFDO0VBQ3BELE1BQU1RLEVBQUUsR0FBRyxJQUFJUixXQUFXLENBQUMsQ0FBQztFQUM1QixJQUFJUyxNQUFNLEdBQUcsS0FBSztFQUNsQixJQUFJQyxPQUFPLEdBQUcscUJBQXFCO0VBQ25DRixFQUFFLENBQUNHLGlCQUFpQixDQUFDLENBQUM7RUFDdEIsSUFBSTtJQUNGSCxFQUFFLENBQUNJLHdCQUF3QixDQUFDLG9CQUFvQixFQUFFLElBQUksRUFBRSxNQUFNLENBQUM7RUFDakUsQ0FBQyxDQUFDLE9BQU9DLEtBQUssRUFBRTtJQUNkLE1BQU1DLFFBQVEsR0FBRyxpRUFBaUU7SUFDbEZKLE9BQU8sR0FBR0csS0FBSyxDQUFDSCxPQUFPO0lBQ3ZCRCxNQUFNLEdBQUdJLEtBQUssQ0FBQ0gsT0FBTyxLQUFLSSxRQUFRO0VBQ3JDO0VBQ0FuQixNQUFNLENBQUNjLE1BQU0sRUFBRUMsT0FBTyxDQUFDO0FBQ3pCLENBQUMsQ0FBQztBQUVGZCxFQUFFLENBQUMsdUVBQXVFLEVBQUUsTUFBTTtFQUFBLElBQUFtQixVQUFBLEVBQUFDLGFBQUEsRUFBQUMsY0FBQTtFQUNoRjtFQUNBLE1BQU1qQixXQUFXLFNBQVNILFFBQVEsQ0FBQztJQUFBO01BQUEsQ0FBQW9CLGNBQUEsRUFBQUYsVUFBQSxJQUFBRyxVQUFBLFNBQUFGLGFBQUEsa0NBQVRuQixRQUFRLEVBQUFzQixDQUFBO0lBQUE7SUFBQSxDQUFBQyxDQUFBLElBQUFMLFVBQUEsUUFBQUUsY0FBQTtJQUFBLE1BQUFELGFBQUEsR0FDL0JsQixRQUFRLENBQUM7TUFDUkssSUFBSSxFQUFFQyxNQUFNO01BQ1ppQixLQUFLLEVBQUUsRUFBRTtNQUNUQyxPQUFPLEVBQUVBLENBQUEsS0FBTSxDQUFDO0lBQ2xCLENBQUMsQ0FBQztNQUFBLGFBQUFGLENBQUE7SUFBQTtJQUFBLElBQ09HLFFBQVFBLENBQUFDLENBQUE7TUFBQSxNQUFBSixDQUFBLEdBQUFJLENBQUE7SUFBQTtFQUNuQjtFQUNBbEIsY0FBYyxDQUFDQyxNQUFNLENBQUMsNEJBQTRCLEVBQUVQLFdBQVcsQ0FBQztFQUNoRSxNQUFNUSxFQUFFLEdBQUcsSUFBSVIsV0FBVyxDQUFDLENBQUM7RUFDNUJRLEVBQUUsQ0FBQ0csaUJBQWlCLENBQUMsQ0FBQztFQUN0QixJQUFJRixNQUFNLEdBQUcsS0FBSztFQUNsQixJQUFJQyxPQUFPLEdBQUcscUJBQXFCO0VBQ25DLElBQUk7SUFDRkYsRUFBRSxDQUFDSSx3QkFBd0IsQ0FBQyxVQUFVLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQztFQUN2RCxDQUFDLENBQUMsT0FBT0MsS0FBSyxFQUFFO0lBQ2QsTUFBTUMsUUFBUSxHQUFHLDRGQUE0RjtJQUM3R0osT0FBTyxHQUFHRyxLQUFLLENBQUNILE9BQU87SUFDdkJELE1BQU0sR0FBR0ksS0FBSyxDQUFDSCxPQUFPLEtBQUtJLFFBQVE7RUFDckM7RUFDQW5CLE1BQU0sQ0FBQ2MsTUFBTSxFQUFFQyxPQUFPLENBQUM7QUFDekIsQ0FBQyxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/test-next/test-attribute-changed-errors.src.js b/test-next/test-attribute-changed-errors.src.js new file mode 100644 index 0000000..2da1485 --- /dev/null +++ b/test-next/test-attribute-changed-errors.src.js @@ -0,0 +1,57 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property } from '../x-element-next.js'; + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Users should use accessor-only decorators (no setter) or private fields +// to achieve read-only behavior. Skip this test as readOnly is not supported. +it.skip('errors are thrown in attributeChangedCallback for read-only properties', () => { + // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. + class TestElement extends XElement { + static get properties() { + return { + readOnlyProperty: { + type: String, + readOnly: true, + }, + }; + } + } + customElements.define('test-element-2', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + el.connectedCallback(); + try { + el.attributeChangedCallback('read-only-property', null, 'nope'); + } catch (error) { + const expected = 'Property "TestElement.prototype.readOnlyProperty" is read-only.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('errors are thrown in attributeChangedCallback for computed properties', () => { + // We cannot try-catch setAttribute, so we fake the attributeChangedCallback. + class TestElement extends XElement { + @property({ + type: String, + input: [], + compute: () => {}, + }) + accessor computed; + } + customElements.define('test-element-attr-computed', TestElement); + const el = new TestElement(); + el.connectedCallback(); + let passed = false; + let message = 'no error was thrown'; + try { + el.attributeChangedCallback('computed', null, 'nope'); + } catch (error) { + const expected = 'Property "TestElement.prototype.computed" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-basic-properties.html b/test-next/test-basic-properties.html new file mode 100644 index 0000000..df6a73b --- /dev/null +++ b/test-next/test-basic-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Basic Properties

+ + + diff --git a/test-next/test-basic-properties.js b/test-next/test-basic-properties.js new file mode 100644 index 0000000..3fae66d --- /dev/null +++ b/test-next/test-basic-properties.js @@ -0,0 +1,703 @@ +let _initProto, _normalPropertyDecs, _init_normalProperty, _objectPropertyDecs, _init_objectProperty, _camelCasePropertyDecs, _init_camelCaseProperty, _numericPropertyDecs, _init_numericProperty, _nullPropertyDecs, _init_nullProperty, _undefinedPropertyDecs, _init_undefinedProperty, _typelessPropertyDecs, _init_typelessProperty, _typelessPropertyWithCustomAttributeDecs, _init_typelessPropertyWithCustomAttribute, _internalPropertyDecs, _init_internalProperty, _get_internalProperty, _set_internalProperty; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +class TestElementNext extends XElement { + static { + [_init_normalProperty, _init_objectProperty, _init_camelCaseProperty, _init_numericProperty, _init_nullProperty, _init_undefinedProperty, _init_typelessProperty, _init_typelessPropertyWithCustomAttribute, _init_internalProperty, _get_internalProperty, _set_internalProperty, _initProto] = _applyDecs(this, [[_normalPropertyDecs, 1, "normalProperty"], [_objectPropertyDecs, 1, "objectProperty"], [_camelCasePropertyDecs, 1, "camelCaseProperty"], [_numericPropertyDecs, 1, "numericProperty"], [_nullPropertyDecs, 1, "nullProperty"], [_undefinedPropertyDecs, 1, "undefinedProperty"], [_typelessPropertyDecs, 1, "typelessProperty"], [_typelessPropertyWithCustomAttributeDecs, 1, "typelessPropertyWithCustomAttribute"], [_internalPropertyDecs, 1, "internalProperty", o => o.#I, (o, v) => o.#I = v]], [], 0, _ => #internalProperty in _, XElement).e; + } + #A = (_initProto(this), _init_normalProperty(this)); + get [(_normalPropertyDecs = property({ + type: String, + initial: 'Ferus' + }), _objectPropertyDecs = property({ + type: Object + }), _camelCasePropertyDecs = property({ + type: String, + initial: 'Bactrian' + }), _numericPropertyDecs = property({ + type: Number, + default: 10 + }), _nullPropertyDecs = property({ + type: String, + default: null + }), _undefinedPropertyDecs = property({ + type: String + }), _typelessPropertyDecs = property({}), _typelessPropertyWithCustomAttributeDecs = property({ + attribute: 'custom-attribute-typeless' + }), _internalPropertyDecs = property({}), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + #B = _init_objectProperty(this); + get objectProperty() { + return this.#B; + } + set objectProperty(v) { + this.#B = v; + } + #C = _init_camelCaseProperty(this); + get camelCaseProperty() { + return this.#C; + } + set camelCaseProperty(v) { + this.#C = v; + } + #D = _init_numericProperty(this); + get numericProperty() { + return this.#D; + } + set numericProperty(v) { + this.#D = v; + } + #E = _init_nullProperty(this); + get nullProperty() { + return this.#E; + } + set nullProperty(v) { + this.#E = v; + } + #F = _init_undefinedProperty(this); + get undefinedProperty() { + return this.#F; + } + set undefinedProperty(v) { + this.#F = v; + } + #G = _init_typelessProperty(this); + get typelessProperty() { + return this.#G; + } + set typelessProperty(v) { + this.#G = v; + } + #H = _init_typelessPropertyWithCustomAttribute(this); + get typelessPropertyWithCustomAttribute() { + return this.#H; + } + set typelessPropertyWithCustomAttribute(v) { + this.#H = v; + } + #I = _init_internalProperty(this); + set #internalProperty(v) { + _set_internalProperty(this, v); + } + get #internalProperty() { + return _get_internalProperty(this); + } + static template(host) { + return html` +
${host.normalProperty}
+ ${host.camelCaseProperty} + ${host.numericProperty} + ${host.nullProperty} + ${host.undefinedProperty} + `; + } +} +customElements.define('test-element-next', TestElementNext); +it('initialization', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('normal').textContent === 'Ferus'); + assert(el.shadowRoot.getElementById('camel').textContent === 'Bactrian'); + assert(el.shadowRoot.getElementById('num').textContent === '10'); + assert(el.shadowRoot.getElementById('nul').textContent === ''); +}); +it('renders an empty string in place of null value', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('nul').textContent === ''); +}); +it('renders an empty string in place of undefined value', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('undef').textContent === ''); +}); +it('property setter updates on next micro tick after connect', async () => { + const el = document.createElement('test-element-next'); + el.camelCaseProperty = 'Nonconforming'; + + // Initial render happens synchronously after initial connection. + document.body.append(el); + assert(el.shadowRoot.getElementById('camel').textContent === 'Nonconforming'); + + // Updates are debounced on a microtask, so they are not immediately seen. + el.camelCaseProperty = 'Dromedary'; + assert(el.shadowRoot.getElementById('camel').textContent === 'Nonconforming'); + + // After the microtask runs, the update is handled. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Dromedary'); +}); +it('property setter renders blank value', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.camelCaseProperty = ''; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === ''); + el.camelCaseProperty = 'Bactrian'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Bactrian'); +}); +it('observes all dash-cased versions of public, typeless, serializable, and declared properties', () => { + const el = document.createElement('test-element-next'); + const expected = ['normal-property', 'camel-case-property', 'numeric-property', 'null-property', 'undefined-property', 'typeless-property', 'custom-attribute-typeless']; + const actual = el.constructor.observedAttributes; + assert(expected.length === actual.length); + assert(expected.every(attribute => actual.includes(attribute))); +}); +it('removeAttribute renders blank', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.removeAttribute('camel-case-property'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + // Note, in general, changing non-reflected properties via attributes can + // be problematic. For example, attributeChangedCallback is not fired if the + // attribute does not change. + assert(el.shadowRoot.getElementById('camel').textContent !== ''); + el.setAttribute('camel-case-property', 'foo'); + el.removeAttribute('camel-case-property'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === ''); +}); +it('setAttribute renders the new value', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.setAttribute('camel-case-property', 'Racing Camel'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Racing Camel'); +}); +it('coerces attributes to the specified type', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.setAttribute('numeric-property', '-99'); + assert(el.numericProperty === -99); +}); +it('allows properties without types', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + for (const value of [{}, 'foo', '5', [], 2]) { + el.typelessProperty = value; + assert(el.typelessProperty === value); + } +}); +it('syncs attributes to properties without types', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const value = '123'; + el.setAttribute('typeless-property', value); + assert(el.typelessProperty === value); +}); +it('initializes from attributes on connect', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('camel-case-property', 'Dromedary'); + document.body.append(el); + assert(el.camelCaseProperty === 'Dromedary'); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it('can use "has" api or "in" operator.', () => { + let _initProto2, _normalPropertyDecs2, _init_normalProperty2; + class TempTestElement extends XElement { + static { + [_init_normalProperty2, _initProto2] = _applyDecs(this, [[_normalPropertyDecs2, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_normalProperty2(this)); + get [(_normalPropertyDecs2 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + if (!('normalProperty' in host) || Reflect.has(host, 'normalProperty') === false) { + throw new Error('The "has" trap does not work.'); + } + return html``; + } + } + customElements.define('temp-test-element-next-1', TempTestElement); + const el = new TempTestElement(); + let passed = false; + let message; + try { + el.connectedCallback(); + passed = true; + } catch (error) { + message = error.message; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, ownKeys doesn’t +// work because properties are simply on the `host.prototype`. +it.skip('can use "ownKeys" api.', () => { + let _initProto3, _normalPropertyDecs3, _init_normalProperty3; + class TempTestElement extends XElement { + static { + [_init_normalProperty3, _initProto3] = _applyDecs(this, [[_normalPropertyDecs3, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto3(this), _init_normalProperty3(this)); + get [(_normalPropertyDecs3 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + const ownKeys = Reflect.ownKeys(host); + if (ownKeys.length !== 1 || ownKeys[0] !== 'normalProperty') { + throw new Error('The "ownKeys" trap does not work.'); + } + return html``; + } + } + customElements.define('temp-test-element-next-2', TempTestElement); + const el = new TempTestElement(); + let passed = false; + let message; + try { + el.connectedCallback(); + passed = true; + } catch (error) { + message = error.message; + } + assert(passed, message); +}); +it('can be read', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.normalProperty === 'Ferus', 'property was read'); + el.normalProperty = 'Dromedary'; + assert(el.normalProperty === 'Dromedary', 'property was written to'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('normal').textContent === 'Dromedary'); +}); +it('inheritance is considered in type checking', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const array = []; + el.objectProperty = array; + assert(el.objectProperty === array, 'property was set'); +}); +it('numeric properties deserialize "" (empty) to "NaN"', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('numeric-property', '0'); + document.body.append(el); + assert(el.numericProperty === 0, '"0" was coerced to 0'); + el.setAttribute('numeric-property', ''); + assert(Number.isNaN(el.numericProperty), '"" was coerced to NaN'); + el.setAttribute('numeric-property', ' '); + assert(Number.isNaN(el.numericProperty), '" " was coerced to NaN'); + el.setAttribute('numeric-property', ' '); + assert(Number.isNaN(el.numericProperty), '" " was coerced to NaN'); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not update properties in the render loop. +it.skip('cannot set to known properties', () => { + let _initProto4, _normalPropertyDecs4, _init_normalProperty4; + class BadTestElement extends XElement { + static { + [_init_normalProperty4, _initProto4] = _applyDecs(this, [[_normalPropertyDecs4, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto4(this), _init_normalProperty4(this)); + get [(_normalPropertyDecs4 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + host.normalProperty = 'Dromedary'; + return html`
${host.normalProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Cannot set "BadTestElement.properties.normalProperty" via "properties".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not update properties in the render loop. +it.skip('cannot set to unknown properties', () => { + let _initProto5, _normalPropertyDecs5, _init_normalProperty5; + class BadTestElement extends XElement { + static { + [_init_normalProperty5, _initProto5] = _applyDecs(this, [[_normalPropertyDecs5, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto5(this), _init_normalProperty5(this)); + get [(_normalPropertyDecs5 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + host.doesNotExist = 'Dromedary'; + return html`
${host.normalProperty}
`; + } + } + customElements.define('bad-test-element-next-2', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not pull objects which don’t exist (or understand +// that it doesn’t exist if it is returning “undefined”). +it.skip('cannot get unknown properties', () => { + let _initProto6, _normalPropertyDecs6, _init_normalProperty6; + class BadTestElement extends XElement { + static { + [_init_normalProperty6, _initProto6] = _applyDecs(this, [[_normalPropertyDecs6, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto6(this), _init_normalProperty6(this)); + get [(_normalPropertyDecs6 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + return html`
${host.doesNotExist}
`; + } + } + customElements.define('bad-test-element-next-3', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test internal proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot set on "internal"', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.normalProperty = 'Dromedary'; + } catch (error) { + const expected = 'Property "TestElement.properties.normalProperty" is publicly available (use normal setter).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. You can of course define a +// new property on an instance like this. This would work even if it was truly +// shadowing an accessor that already exists. +it.skip('cannot "defineProperty" on properties.', () => { + let _initProto7, _normalPropertyDecs7, _init_normalProperty7; + class BadTestElement extends XElement { + static { + [_init_normalProperty7, _initProto7] = _applyDecs(this, [[_normalPropertyDecs7, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto7(this), _init_normalProperty7(this)); + get [(_normalPropertyDecs7 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.defineProperty(host, 'foo', {}); + return html``; + } + } + customElements.define('bad-test-element-next-4', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. You can of course call delete +// on an instance like this — but, this doesn’t break the accessors in any way. +it.skip('cannot "delete" on properties.', () => { + let _initProto8, _normalPropertyDecs8, _init_normalProperty8; + class BadTestElement extends XElement { + static { + [_init_normalProperty8, _initProto8] = _applyDecs(this, [[_normalPropertyDecs8, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto8(this), _init_normalProperty8(this)); + get [(_normalPropertyDecs8 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.deleteProperty(host, 'normalProperty'); + return html``; + } + } + customElements.define('bad-test-element-next-5', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "getOwnPropertyDescriptor" on properties.', () => { + let _initProto9, _normalPropertyDecs9, _init_normalProperty9; + class BadTestElement extends XElement { + static { + [_init_normalProperty9, _initProto9] = _applyDecs(this, [[_normalPropertyDecs9, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto9(this), _init_normalProperty9(this)); + get [(_normalPropertyDecs9 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.getOwnPropertyDescriptor(host, 'normalProperty'); + return html``; + } + } + customElements.define('bad-test-element-next-6', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "getPrototypeOf" on properties.', () => { + let _initProto0, _normalPropertyDecs0, _init_normalProperty0; + class BadTestElement extends XElement { + static { + [_init_normalProperty0, _initProto0] = _applyDecs(this, [[_normalPropertyDecs0, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto0(this), _init_normalProperty0(this)); + get [(_normalPropertyDecs0 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.getPrototypeOf(host); + return html``; + } + } + customElements.define('bad-test-element-next-7', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "isExtensible" on properties.', () => { + let _initProto1, _normalPropertyDecs1, _init_normalProperty1; + class BadTestElement extends XElement { + static { + [_init_normalProperty1, _initProto1] = _applyDecs(this, [[_normalPropertyDecs1, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto1(this), _init_normalProperty1(this)); + get [(_normalPropertyDecs1 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.isExtensible(host); + return html``; + } + } + customElements.define('bad-test-element-next-8', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "preventExtensions" on properties.', () => { + let _initProto10, _normalPropertyDecs10, _init_normalProperty10; + class BadTestElement extends XElement { + static { + [_init_normalProperty10, _initProto10] = _applyDecs(this, [[_normalPropertyDecs10, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto10(this), _init_normalProperty10(this)); + get [(_normalPropertyDecs10 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.preventExtensions(host); + return html``; + } + } + customElements.define('bad-test-element-next-9', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "setPrototypeOf" on properties.', () => { + let _initProto11, _normalPropertyDecs11, _init_normalProperty11; + class BadTestElement extends XElement { + static { + [_init_normalProperty11, _initProto11] = _applyDecs(this, [[_normalPropertyDecs11, 1, "normalProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto11(this), _init_normalProperty11(this)); + get [(_normalPropertyDecs11 = property({ + type: String + }), "normalProperty")]() { + return this.#A; + } + set normalProperty(v) { + this.#A = v; + } + static template(host) { + Reflect.setPrototypeOf(host, Array); + return html``; + } + } + customElements.define('bad-test-element-next-10', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnROZXh0IiwiX2luaXRfbm9ybWFsUHJvcGVydHkiLCJfaW5pdF9vYmplY3RQcm9wZXJ0eSIsIl9pbml0X2NhbWVsQ2FzZVByb3BlcnR5IiwiX2luaXRfbnVtZXJpY1Byb3BlcnR5IiwiX2luaXRfbnVsbFByb3BlcnR5IiwiX2luaXRfdW5kZWZpbmVkUHJvcGVydHkiLCJfaW5pdF90eXBlbGVzc1Byb3BlcnR5IiwiX2luaXRfdHlwZWxlc3NQcm9wZXJ0eVdpdGhDdXN0b21BdHRyaWJ1dGUiLCJfaW5pdF9pbnRlcm5hbFByb3BlcnR5IiwiX2dldF9pbnRlcm5hbFByb3BlcnR5IiwiX3NldF9pbnRlcm5hbFByb3BlcnR5IiwiX2luaXRQcm90byIsIl9hcHBseURlY3MiLCJfbm9ybWFsUHJvcGVydHlEZWNzIiwiX29iamVjdFByb3BlcnR5RGVjcyIsIl9jYW1lbENhc2VQcm9wZXJ0eURlY3MiLCJfbnVtZXJpY1Byb3BlcnR5RGVjcyIsIl9udWxsUHJvcGVydHlEZWNzIiwiX3VuZGVmaW5lZFByb3BlcnR5RGVjcyIsIl90eXBlbGVzc1Byb3BlcnR5RGVjcyIsIl90eXBlbGVzc1Byb3BlcnR5V2l0aEN1c3RvbUF0dHJpYnV0ZURlY3MiLCJfaW50ZXJuYWxQcm9wZXJ0eURlY3MiLCJvIiwiSSIsInYiLCJfIiwiaW50ZXJuYWxQcm9wZXJ0eSIsImUiLCJBIiwidHlwZSIsIlN0cmluZyIsImluaXRpYWwiLCJPYmplY3QiLCJOdW1iZXIiLCJkZWZhdWx0IiwiYXR0cmlidXRlIiwibm9ybWFsUHJvcGVydHkiLCJCIiwib2JqZWN0UHJvcGVydHkiLCJDIiwiY2FtZWxDYXNlUHJvcGVydHkiLCJEIiwibnVtZXJpY1Byb3BlcnR5IiwiRSIsIm51bGxQcm9wZXJ0eSIsIkYiLCJ1bmRlZmluZWRQcm9wZXJ0eSIsIkciLCJ0eXBlbGVzc1Byb3BlcnR5IiwiSCIsInR5cGVsZXNzUHJvcGVydHlXaXRoQ3VzdG9tQXR0cmlidXRlIiwiI2ludGVybmFsUHJvcGVydHkiLCJ0ZW1wbGF0ZSIsImhvc3QiLCJjdXN0b21FbGVtZW50cyIsImRlZmluZSIsImVsIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwiYm9keSIsImFwcGVuZCIsInNoYWRvd1Jvb3QiLCJnZXRFbGVtZW50QnlJZCIsInRleHRDb250ZW50IiwiUHJvbWlzZSIsInJlc29sdmUiLCJleHBlY3RlZCIsImFjdHVhbCIsImNvbnN0cnVjdG9yIiwib2JzZXJ2ZWRBdHRyaWJ1dGVzIiwibGVuZ3RoIiwiZXZlcnkiLCJpbmNsdWRlcyIsInJlbW92ZUF0dHJpYnV0ZSIsInNldEF0dHJpYnV0ZSIsInZhbHVlIiwiX2luaXRQcm90bzIiLCJfbm9ybWFsUHJvcGVydHlEZWNzMiIsIl9pbml0X25vcm1hbFByb3BlcnR5MiIsIlRlbXBUZXN0RWxlbWVudCIsIlJlZmxlY3QiLCJoYXMiLCJFcnJvciIsInBhc3NlZCIsIm1lc3NhZ2UiLCJjb25uZWN0ZWRDYWxsYmFjayIsImVycm9yIiwic2tpcCIsIl9pbml0UHJvdG8zIiwiX25vcm1hbFByb3BlcnR5RGVjczMiLCJfaW5pdF9ub3JtYWxQcm9wZXJ0eTMiLCJvd25LZXlzIiwiYXJyYXkiLCJpc05hTiIsIl9pbml0UHJvdG80IiwiX25vcm1hbFByb3BlcnR5RGVjczQiLCJfaW5pdF9ub3JtYWxQcm9wZXJ0eTQiLCJCYWRUZXN0RWxlbWVudCIsIl9pbml0UHJvdG81IiwiX25vcm1hbFByb3BlcnR5RGVjczUiLCJfaW5pdF9ub3JtYWxQcm9wZXJ0eTUiLCJkb2VzTm90RXhpc3QiLCJfaW5pdFByb3RvNiIsIl9ub3JtYWxQcm9wZXJ0eURlY3M2IiwiX2luaXRfbm9ybWFsUHJvcGVydHk2IiwiaW50ZXJuYWwiLCJfaW5pdFByb3RvNyIsIl9ub3JtYWxQcm9wZXJ0eURlY3M3IiwiX2luaXRfbm9ybWFsUHJvcGVydHk3IiwiZGVmaW5lUHJvcGVydHkiLCJfaW5pdFByb3RvOCIsIl9ub3JtYWxQcm9wZXJ0eURlY3M4IiwiX2luaXRfbm9ybWFsUHJvcGVydHk4IiwiZGVsZXRlUHJvcGVydHkiLCJfaW5pdFByb3RvOSIsIl9ub3JtYWxQcm9wZXJ0eURlY3M5IiwiX2luaXRfbm9ybWFsUHJvcGVydHk5IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiX2luaXRQcm90bzAiLCJfbm9ybWFsUHJvcGVydHlEZWNzMCIsIl9pbml0X25vcm1hbFByb3BlcnR5MCIsImdldFByb3RvdHlwZU9mIiwiX2luaXRQcm90bzEiLCJfbm9ybWFsUHJvcGVydHlEZWNzMSIsIl9pbml0X25vcm1hbFByb3BlcnR5MSIsImlzRXh0ZW5zaWJsZSIsIl9pbml0UHJvdG8xMCIsIl9ub3JtYWxQcm9wZXJ0eURlY3MxMCIsIl9pbml0X25vcm1hbFByb3BlcnR5MTAiLCJwcmV2ZW50RXh0ZW5zaW9ucyIsIl9pbml0UHJvdG8xMSIsIl9ub3JtYWxQcm9wZXJ0eURlY3MxMSIsIl9pbml0X25vcm1hbFByb3BlcnR5MTEiLCJzZXRQcm90b3R5cGVPZiIsIkFycmF5Il0sInNvdXJjZXMiOlsidGVzdC1iYXNpYy1wcm9wZXJ0aWVzLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHksIGh0bWwgfSBmcm9tICcuLi94LWVsZW1lbnQtbmV4dC5qcyc7XG5cbmNsYXNzIFRlc3RFbGVtZW50TmV4dCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAnRmVydXMnIH0pXG4gIGFjY2Vzc29yIG5vcm1hbFByb3BlcnR5O1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE9iamVjdCB9KVxuICBhY2Nlc3NvciBvYmplY3RQcm9wZXJ0eTtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdCYWN0cmlhbicgfSlcbiAgYWNjZXNzb3IgY2FtZWxDYXNlUHJvcGVydHk7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBkZWZhdWx0OiAxMCB9KVxuICBhY2Nlc3NvciBudW1lcmljUHJvcGVydHk7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBkZWZhdWx0OiBudWxsIH0pXG4gIGFjY2Vzc29yIG51bGxQcm9wZXJ0eTtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgYWNjZXNzb3IgdW5kZWZpbmVkUHJvcGVydHk7XG5cbiAgQHByb3BlcnR5KHt9KVxuICBhY2Nlc3NvciB0eXBlbGVzc1Byb3BlcnR5O1xuXG4gIEBwcm9wZXJ0eSh7IGF0dHJpYnV0ZTogJ2N1c3RvbS1hdHRyaWJ1dGUtdHlwZWxlc3MnIH0pXG4gIGFjY2Vzc29yIHR5cGVsZXNzUHJvcGVydHlXaXRoQ3VzdG9tQXR0cmlidXRlO1xuXG4gIEBwcm9wZXJ0eSh7fSlcbiAgYWNjZXNzb3IgI2ludGVybmFsUHJvcGVydHk7XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGBcbiAgICAgIDxkaXYgaWQ9XCJub3JtYWxcIj4ke2hvc3Qubm9ybWFsUHJvcGVydHl9PC9kaXY+XG4gICAgICA8c3BhbiBpZD1cImNhbWVsXCI+JHtob3N0LmNhbWVsQ2FzZVByb3BlcnR5fTwvc3Bhbj5cbiAgICAgIDxzcGFuIGlkPVwibnVtXCI+JHtob3N0Lm51bWVyaWNQcm9wZXJ0eX08L3NwYW4+XG4gICAgICA8c3BhbiBpZD1cIm51bFwiPiR7aG9zdC5udWxsUHJvcGVydHl9PC9zcGFuPlxuICAgICAgPHNwYW4gaWQ9XCJ1bmRlZlwiPiR7aG9zdC51bmRlZmluZWRQcm9wZXJ0eX08L3NwYW4+XG4gICAgYDtcbiAgfVxufVxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtbmV4dCcsIFRlc3RFbGVtZW50TmV4dCk7XG5cbml0KCdpbml0aWFsaXphdGlvbicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdub3JtYWwnKS50ZXh0Q29udGVudCA9PT0gJ0ZlcnVzJyk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdjYW1lbCcpLnRleHRDb250ZW50ID09PSAnQmFjdHJpYW4nKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ251bScpLnRleHRDb250ZW50ID09PSAnMTAnKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ251bCcpLnRleHRDb250ZW50ID09PSAnJyk7XG59KTtcblxuaXQoJ3JlbmRlcnMgYW4gZW1wdHkgc3RyaW5nIGluIHBsYWNlIG9mIG51bGwgdmFsdWUnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC5nZXRFbGVtZW50QnlJZCgnbnVsJykudGV4dENvbnRlbnQgPT09ICcnKTtcbn0pO1xuXG5pdCgncmVuZGVycyBhbiBlbXB0eSBzdHJpbmcgaW4gcGxhY2Ugb2YgdW5kZWZpbmVkIHZhbHVlJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ3VuZGVmJykudGV4dENvbnRlbnQgPT09ICcnKTtcbn0pO1xuXG5pdCgncHJvcGVydHkgc2V0dGVyIHVwZGF0ZXMgb24gbmV4dCBtaWNybyB0aWNrIGFmdGVyIGNvbm5lY3QnLCBhc3luYyAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZWwuY2FtZWxDYXNlUHJvcGVydHkgPSAnTm9uY29uZm9ybWluZyc7XG5cbiAgLy8gSW5pdGlhbCByZW5kZXIgaGFwcGVucyBzeW5jaHJvbm91c2x5IGFmdGVyIGluaXRpYWwgY29ubmVjdGlvbi5cbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC5nZXRFbGVtZW50QnlJZCgnY2FtZWwnKS50ZXh0Q29udGVudCA9PT0gJ05vbmNvbmZvcm1pbmcnKTtcblxuICAvLyBVcGRhdGVzIGFyZSBkZWJvdW5jZWQgb24gYSBtaWNyb3Rhc2ssIHNvIHRoZXkgYXJlIG5vdCBpbW1lZGlhdGVseSBzZWVuLlxuICBlbC5jYW1lbENhc2VQcm9wZXJ0eSA9ICdEcm9tZWRhcnknO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC5nZXRFbGVtZW50QnlJZCgnY2FtZWwnKS50ZXh0Q29udGVudCA9PT0gJ05vbmNvbmZvcm1pbmcnKTtcblxuICAvLyBBZnRlciB0aGUgbWljcm90YXNrIHJ1bnMsIHRoZSB1cGRhdGUgaXMgaGFuZGxlZC5cbiAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdjYW1lbCcpLnRleHRDb250ZW50ID09PSAnRHJvbWVkYXJ5Jyk7XG59KTtcblxuaXQoJ3Byb3BlcnR5IHNldHRlciByZW5kZXJzIGJsYW5rIHZhbHVlJywgYXN5bmMgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgZWwuY2FtZWxDYXNlUHJvcGVydHkgPSAnJztcblxuICAvLyBXZSBtdXN0IGF3YWl0IGEgbWljcm90YXNrIGZvciB0aGUgdXBkYXRlIHRvIHRha2UgcGxhY2UuXG4gIGF3YWl0IFByb21pc2UucmVzb2x2ZSgpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC5nZXRFbGVtZW50QnlJZCgnY2FtZWwnKS50ZXh0Q29udGVudCA9PT0gJycpO1xuICBlbC5jYW1lbENhc2VQcm9wZXJ0eSA9ICdCYWN0cmlhbic7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2NhbWVsJykudGV4dENvbnRlbnQgPT09ICdCYWN0cmlhbicpO1xufSk7XG5cbml0KCdvYnNlcnZlcyBhbGwgZGFzaC1jYXNlZCB2ZXJzaW9ucyBvZiBwdWJsaWMsIHR5cGVsZXNzLCBzZXJpYWxpemFibGUsIGFuZCBkZWNsYXJlZCBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGNvbnN0IGV4cGVjdGVkID0gW1xuICAgICdub3JtYWwtcHJvcGVydHknLFxuICAgICdjYW1lbC1jYXNlLXByb3BlcnR5JyxcbiAgICAnbnVtZXJpYy1wcm9wZXJ0eScsXG4gICAgJ251bGwtcHJvcGVydHknLFxuICAgICd1bmRlZmluZWQtcHJvcGVydHknLFxuICAgICd0eXBlbGVzcy1wcm9wZXJ0eScsXG4gICAgJ2N1c3RvbS1hdHRyaWJ1dGUtdHlwZWxlc3MnLFxuICBdO1xuICBjb25zdCBhY3R1YWwgPSBlbC5jb25zdHJ1Y3Rvci5vYnNlcnZlZEF0dHJpYnV0ZXM7XG4gIGFzc2VydChleHBlY3RlZC5sZW5ndGggPT09IGFjdHVhbC5sZW5ndGgpO1xuICBhc3NlcnQoZXhwZWN0ZWQuZXZlcnkoYXR0cmlidXRlID0+IGFjdHVhbC5pbmNsdWRlcyhhdHRyaWJ1dGUpKSk7XG59KTtcblxuaXQoJ3JlbW92ZUF0dHJpYnV0ZSByZW5kZXJzIGJsYW5rJywgYXN5bmMgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgZWwucmVtb3ZlQXR0cmlidXRlKCdjYW1lbC1jYXNlLXByb3BlcnR5Jyk7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgLy8gTm90ZSwgaW4gZ2VuZXJhbCwgY2hhbmdpbmcgbm9uLXJlZmxlY3RlZCBwcm9wZXJ0aWVzIHZpYSBhdHRyaWJ1dGVzIGNhblxuICAvLyBiZSBwcm9ibGVtYXRpYy4gRm9yIGV4YW1wbGUsIGF0dHJpYnV0ZUNoYW5nZWRDYWxsYmFjayBpcyBub3QgZmlyZWQgaWYgdGhlXG4gIC8vIGF0dHJpYnV0ZSBkb2VzIG5vdCBjaGFuZ2UuXG4gIGFzc2VydChlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdjYW1lbCcpLnRleHRDb250ZW50ICE9PSAnJyk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnY2FtZWwtY2FzZS1wcm9wZXJ0eScsICdmb28nKTtcbiAgZWwucmVtb3ZlQXR0cmlidXRlKCdjYW1lbC1jYXNlLXByb3BlcnR5Jyk7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2NhbWVsJykudGV4dENvbnRlbnQgPT09ICcnKTtcbn0pO1xuXG5pdCgnc2V0QXR0cmlidXRlIHJlbmRlcnMgdGhlIG5ldyB2YWx1ZScsIGFzeW5jICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnY2FtZWwtY2FzZS1wcm9wZXJ0eScsICdSYWNpbmcgQ2FtZWwnKTtcblxuICAvLyBXZSBtdXN0IGF3YWl0IGEgbWljcm90YXNrIGZvciB0aGUgdXBkYXRlIHRvIHRha2UgcGxhY2UuXG4gIGF3YWl0IFByb21pc2UucmVzb2x2ZSgpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC5nZXRFbGVtZW50QnlJZCgnY2FtZWwnKS50ZXh0Q29udGVudCA9PT0gJ1JhY2luZyBDYW1lbCcpO1xufSk7XG5cbml0KCdjb2VyY2VzIGF0dHJpYnV0ZXMgdG8gdGhlIHNwZWNpZmllZCB0eXBlJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgZWwuc2V0QXR0cmlidXRlKCdudW1lcmljLXByb3BlcnR5JywgJy05OScpO1xuICBhc3NlcnQoZWwubnVtZXJpY1Byb3BlcnR5ID09PSAtOTkpO1xufSk7XG5cbml0KCdhbGxvd3MgcHJvcGVydGllcyB3aXRob3V0IHR5cGVzJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgZm9yIChjb25zdCB2YWx1ZSBvZiBbe30sICdmb28nLCAnNScsIFtdLCAyXSkge1xuICAgIGVsLnR5cGVsZXNzUHJvcGVydHkgPSB2YWx1ZTtcbiAgICBhc3NlcnQoZWwudHlwZWxlc3NQcm9wZXJ0eSA9PT0gdmFsdWUpO1xuICB9XG59KTtcblxuaXQoJ3N5bmNzIGF0dHJpYnV0ZXMgdG8gcHJvcGVydGllcyB3aXRob3V0IHR5cGVzJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgY29uc3QgdmFsdWUgPSAnMTIzJztcbiAgZWwuc2V0QXR0cmlidXRlKCd0eXBlbGVzcy1wcm9wZXJ0eScsIHZhbHVlKTtcbiAgYXNzZXJ0KGVsLnR5cGVsZXNzUHJvcGVydHkgPT09IHZhbHVlKTtcbn0pO1xuXG5pdCgnaW5pdGlhbGl6ZXMgZnJvbSBhdHRyaWJ1dGVzIG9uIGNvbm5lY3QnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZWwuc2V0QXR0cmlidXRlKCdjYW1lbC1jYXNlLXByb3BlcnR5JywgJ0Ryb21lZGFyeScpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5jYW1lbENhc2VQcm9wZXJ0eSA9PT0gJ0Ryb21lZGFyeScpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC5cbml0KCdjYW4gdXNlIFwiaGFzXCIgYXBpIG9yIFwiaW5cIiBvcGVyYXRvci4nLCAoKSA9PiB7XG4gIGNsYXNzIFRlbXBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBpZiAoISgnbm9ybWFsUHJvcGVydHknIGluIGhvc3QpIHx8IFJlZmxlY3QuaGFzKGhvc3QsICdub3JtYWxQcm9wZXJ0eScpID09PSBmYWxzZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoZSBcImhhc1wiIHRyYXAgZG9lcyBub3Qgd29yay4nKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBodG1sYGA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVtcC10ZXN0LWVsZW1lbnQtbmV4dC0xJywgVGVtcFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVtcFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2U7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgICBwYXNzZWQgPSB0cnVlO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC4gSW4gdGhpcyBjYXNlLCBvd25LZXlzIGRvZXNu4oCZdFxuLy8gIHdvcmsgYmVjYXVzZSBwcm9wZXJ0aWVzIGFyZSBzaW1wbHkgb24gdGhlIGBob3N0LnByb3RvdHlwZWAuXG5pdC5za2lwKCdjYW4gdXNlIFwib3duS2V5c1wiIGFwaS4nLCAoKSA9PiB7XG4gIGNsYXNzIFRlbXBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBjb25zdCBvd25LZXlzID0gUmVmbGVjdC5vd25LZXlzKGhvc3QpO1xuICAgICAgaWYgKG93bktleXMubGVuZ3RoICE9PSAxIHx8IG93bktleXNbMF0gIT09ICdub3JtYWxQcm9wZXJ0eScpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUaGUgXCJvd25LZXlzXCIgdHJhcCBkb2VzIG5vdCB3b3JrLicpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGh0bWxgYDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZW1wLXRlc3QtZWxlbWVudC1uZXh0LTInLCBUZW1wVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBUZW1wVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZTtcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICAgIHBhc3NlZCA9IHRydWU7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2NhbiBiZSByZWFkJywgYXN5bmMgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLm5vcm1hbFByb3BlcnR5ID09PSAnRmVydXMnLCAncHJvcGVydHkgd2FzIHJlYWQnKTtcbiAgZWwubm9ybWFsUHJvcGVydHkgPSAnRHJvbWVkYXJ5JztcbiAgYXNzZXJ0KGVsLm5vcm1hbFByb3BlcnR5ID09PSAnRHJvbWVkYXJ5JywgJ3Byb3BlcnR5IHdhcyB3cml0dGVuIHRvJyk7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ25vcm1hbCcpLnRleHRDb250ZW50ID09PSAnRHJvbWVkYXJ5Jyk7XG59KTtcblxuaXQoJ2luaGVyaXRhbmNlIGlzIGNvbnNpZGVyZWQgaW4gdHlwZSBjaGVja2luZycsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGNvbnN0IGFycmF5ID0gW107XG4gIGVsLm9iamVjdFByb3BlcnR5ID0gYXJyYXk7XG4gIGFzc2VydChlbC5vYmplY3RQcm9wZXJ0eSA9PT0gYXJyYXksICdwcm9wZXJ0eSB3YXMgc2V0Jyk7XG59KTtcblxuaXQoJ251bWVyaWMgcHJvcGVydGllcyBkZXNlcmlhbGl6ZSBcIlwiIChlbXB0eSkgdG8gXCJOYU5cIicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ251bWVyaWMtcHJvcGVydHknLCAnMCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5udW1lcmljUHJvcGVydHkgPT09IDAsICdcIjBcIiB3YXMgY29lcmNlZCB0byAwJyk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnbnVtZXJpYy1wcm9wZXJ0eScsICcnKTtcbiAgYXNzZXJ0KE51bWJlci5pc05hTihlbC5udW1lcmljUHJvcGVydHkpLCAnXCJcIiB3YXMgY29lcmNlZCB0byBOYU4nKTtcbiAgZWwuc2V0QXR0cmlidXRlKCdudW1lcmljLXByb3BlcnR5JywgJyAnKTtcbiAgYXNzZXJ0KE51bWJlci5pc05hTihlbC5udW1lcmljUHJvcGVydHkpLCAnXCIgXCIgd2FzIGNvZXJjZWQgdG8gTmFOJyk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnbnVtZXJpYy1wcm9wZXJ0eScsICcgICAgICAnKTtcbiAgYXNzZXJ0KE51bWJlci5pc05hTihlbC5udW1lcmljUHJvcGVydHkpLCAnXCIgICAgICBcIiB3YXMgY29lcmNlZCB0byBOYU4nKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IHByb3BlcnRpZXMgcHJveHksIGJ1dCBpdFxuLy8gIG5vdyBqdXN0IGNoZWNrcyBiYXNpYywgaWRpb21hdGljIEphdmFTY3JpcHQuIEluIHRoaXMgY2FzZSwgd2UgZXhwZWN0IG91clxuLy8gIHVzZXJzIHRvIGJlIGV4cGVydHMgYW5kIG5vdCB1cGRhdGUgcHJvcGVydGllcyBpbiB0aGUgcmVuZGVyIGxvb3AuXG5pdC5za2lwKCdjYW5ub3Qgc2V0IHRvIGtub3duIHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNsYXNzIEJhZFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgIGFjY2Vzc29yIG5vcm1hbFByb3BlcnR5O1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIGhvc3Qubm9ybWFsUHJvcGVydHkgPSAnRHJvbWVkYXJ5JztcbiAgICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm5vcm1hbFByb3BlcnR5fTwvZGl2PmA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTEnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdDYW5ub3Qgc2V0IFwiQmFkVGVzdEVsZW1lbnQucHJvcGVydGllcy5ub3JtYWxQcm9wZXJ0eVwiIHZpYSBcInByb3BlcnRpZXNcIi4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC4gSW4gdGhpcyBjYXNlLCB3ZSBleHBlY3Qgb3VyXG4vLyAgdXNlcnMgdG8gYmUgZXhwZXJ0cyBhbmQgbm90IHVwZGF0ZSBwcm9wZXJ0aWVzIGluIHRoZSByZW5kZXIgbG9vcC5cbml0LnNraXAoJ2Nhbm5vdCBzZXQgdG8gdW5rbm93biBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBob3N0LmRvZXNOb3RFeGlzdCA9ICdEcm9tZWRhcnknO1xuICAgICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3Qubm9ybWFsUHJvcGVydHl9PC9kaXY+YDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdiYWQtdGVzdC1lbGVtZW50LW5leHQtMicsIEJhZFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgQmFkVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Byb3BlcnR5IFwiQmFkVGVzdEVsZW1lbnQucHJvcGVydGllcy5kb2VzTm90RXhpc3RcIiBkb2VzIG5vdCBleGlzdC4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC4gSW4gdGhpcyBjYXNlLCB3ZSBleHBlY3Qgb3VyXG4vLyAgdXNlcnMgdG8gYmUgZXhwZXJ0cyBhbmQgbm90IHB1bGwgb2JqZWN0cyB3aGljaCBkb27igJl0IGV4aXN0IChvciB1bmRlcnN0YW5kXG4vLyAgdGhhdCBpdCBkb2VzbuKAmXQgZXhpc3QgaWYgaXQgaXMgcmV0dXJuaW5nIOKAnHVuZGVmaW5lZOKAnSkuXG5pdC5za2lwKCdjYW5ub3QgZ2V0IHVua25vd24gcHJvcGVydGllcycsICgpID0+IHtcbiAgY2xhc3MgQmFkVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nIH0pXG4gICAgYWNjZXNzb3Igbm9ybWFsUHJvcGVydHk7XG5cbiAgICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3QuZG9lc05vdEV4aXN0fTwvZGl2PmA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTMnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIkJhZFRlc3RFbGVtZW50LnByb3BlcnRpZXMuZG9lc05vdEV4aXN0XCIgZG9lcyBub3QgZXhpc3QuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IGludGVybmFsIHByb3h5LCBidXQgaXRcbi8vICBub3cganVzdCBjaGVja3MgYmFzaWMsIGlkaW9tYXRpYyBKYXZhU2NyaXB0LlxuaXQuc2tpcCgnY2Fubm90IHNldCBvbiBcImludGVybmFsXCInLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmludGVybmFsLm5vcm1hbFByb3BlcnR5ID0gJ0Ryb21lZGFyeSc7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnUHJvcGVydHkgXCJUZXN0RWxlbWVudC5wcm9wZXJ0aWVzLm5vcm1hbFByb3BlcnR5XCIgaXMgcHVibGljbHkgYXZhaWxhYmxlICh1c2Ugbm9ybWFsIHNldHRlcikuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IHByb3BlcnRpZXMgcHJveHksIGJ1dCBpdFxuLy8gIG5vdyBqdXN0IGNoZWNrcyBiYXNpYywgaWRpb21hdGljIEphdmFTY3JpcHQuIFlvdSBjYW4gb2YgY291cnNlIGRlZmluZSBhXG4vLyAgbmV3IHByb3BlcnR5IG9uIGFuIGluc3RhbmNlIGxpa2UgdGhpcy4gVGhpcyB3b3VsZCB3b3JrIGV2ZW4gaWYgaXQgd2FzIHRydWx5XG4vLyAgc2hhZG93aW5nIGFuIGFjY2Vzc29yIHRoYXQgYWxyZWFkeSBleGlzdHMuXG5pdC5za2lwKCdjYW5ub3QgXCJkZWZpbmVQcm9wZXJ0eVwiIG9uIHByb3BlcnRpZXMuJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBSZWZsZWN0LmRlZmluZVByb3BlcnR5KGhvc3QsICdmb28nLCB7fSk7XG4gICAgICByZXR1cm4gaHRtbGBgO1xuICAgIH1cbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ2JhZC10ZXN0LWVsZW1lbnQtbmV4dC00JywgQmFkVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBCYWRUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmNvbm5lY3RlZENhbGxiYWNrKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgcHJvcGVydGllcyBwcm94eS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC4gWW91IGNhbiBvZiBjb3Vyc2UgY2FsbCBkZWxldGVcbi8vICBvbiBhbiBpbnN0YW5jZSBsaWtlIHRoaXMg4oCUIGJ1dCwgdGhpcyBkb2VzbuKAmXQgYnJlYWsgdGhlIGFjY2Vzc29ycyBpbiBhbnkgd2F5LlxuaXQuc2tpcCgnY2Fubm90IFwiZGVsZXRlXCIgb24gcHJvcGVydGllcy4nLCAoKSA9PiB7XG4gIGNsYXNzIEJhZFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgIGFjY2Vzc29yIG5vcm1hbFByb3BlcnR5O1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIFJlZmxlY3QuZGVsZXRlUHJvcGVydHkoaG9zdCwgJ25vcm1hbFByb3BlcnR5Jyk7XG4gICAgICByZXR1cm4gaHRtbGBgO1xuICAgIH1cbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ2JhZC10ZXN0LWVsZW1lbnQtbmV4dC01JywgQmFkVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBCYWRUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmNvbm5lY3RlZENhbGxiYWNrKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgcHJvcGVydGllcyBwcm94eS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcHJvcGVydGllcyBwcm94eSwgYnV0IGl0XG4vLyAgbm93IGp1c3QgY2hlY2tzIGJhc2ljLCBpZGlvbWF0aWMgSmF2YVNjcmlwdC5cbml0LnNraXAoJ2Nhbm5vdCBcImdldE93blByb3BlcnR5RGVzY3JpcHRvclwiIG9uIHByb3BlcnRpZXMuJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBSZWZsZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihob3N0LCAnbm9ybWFsUHJvcGVydHknKTtcbiAgICAgIHJldHVybiBodG1sYGA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTYnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdJbnZhbGlkIHVzZSBvZiBwcm9wZXJ0aWVzIHByb3h5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBwcm9wZXJ0aWVzIHByb3h5LCBidXQgaXRcbi8vICBub3cganVzdCBjaGVja3MgYmFzaWMsIGlkaW9tYXRpYyBKYXZhU2NyaXB0LlxuaXQuc2tpcCgnY2Fubm90IFwiZ2V0UHJvdG90eXBlT2ZcIiBvbiBwcm9wZXJ0aWVzLicsICgpID0+IHtcbiAgY2xhc3MgQmFkVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nIH0pXG4gICAgYWNjZXNzb3Igbm9ybWFsUHJvcGVydHk7XG5cbiAgICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgICAgUmVmbGVjdC5nZXRQcm90b3R5cGVPZihob3N0KTtcbiAgICAgIHJldHVybiBodG1sYGA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTcnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdJbnZhbGlkIHVzZSBvZiBwcm9wZXJ0aWVzIHByb3h5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBwcm9wZXJ0aWVzIHByb3h5LCBidXQgaXRcbi8vICBub3cganVzdCBjaGVja3MgYmFzaWMsIGlkaW9tYXRpYyBKYXZhU2NyaXB0LlxuaXQuc2tpcCgnY2Fubm90IFwiaXNFeHRlbnNpYmxlXCIgb24gcHJvcGVydGllcy4nLCAoKSA9PiB7XG4gIGNsYXNzIEJhZFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgIGFjY2Vzc29yIG5vcm1hbFByb3BlcnR5O1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIFJlZmxlY3QuaXNFeHRlbnNpYmxlKGhvc3QpO1xuICAgICAgcmV0dXJuIGh0bWxgYDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdiYWQtdGVzdC1lbGVtZW50LW5leHQtOCcsIEJhZFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgQmFkVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdXNlIG9mIHByb3BlcnRpZXMgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IHByb3BlcnRpZXMgcHJveHksIGJ1dCBpdFxuLy8gIG5vdyBqdXN0IGNoZWNrcyBiYXNpYywgaWRpb21hdGljIEphdmFTY3JpcHQuXG5pdC5za2lwKCdjYW5ub3QgXCJwcmV2ZW50RXh0ZW5zaW9uc1wiIG9uIHByb3BlcnRpZXMuJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBSZWZsZWN0LnByZXZlbnRFeHRlbnNpb25zKGhvc3QpO1xuICAgICAgcmV0dXJuIGh0bWxgYDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdiYWQtdGVzdC1lbGVtZW50LW5leHQtOScsIEJhZFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgQmFkVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdXNlIG9mIHByb3BlcnRpZXMgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IHByb3BlcnRpZXMgcHJveHksIGJ1dCBpdFxuLy8gIG5vdyBqdXN0IGNoZWNrcyBiYXNpYywgaWRpb21hdGljIEphdmFTY3JpcHQuXG5pdC5za2lwKCdjYW5ub3QgXCJzZXRQcm90b3R5cGVPZlwiIG9uIHByb3BlcnRpZXMuJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcgfSlcbiAgICBhY2Nlc3NvciBub3JtYWxQcm9wZXJ0eTtcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICBSZWZsZWN0LnNldFByb3RvdHlwZU9mKGhvc3QsIEFycmF5KTtcbiAgICAgIHJldHVybiBodG1sYGA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTEwJywgQmFkVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBCYWRUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmNvbm5lY3RlZENhbGxiYWNrKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgcHJvcGVydGllcyBwcm94eS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLFNBQVNBLE1BQU0sRUFBRUMsRUFBRSxRQUFRLDJCQUEyQjtBQUN0RCxTQUFTQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsSUFBSSxRQUFRLHNCQUFzQjtBQUUvRCxNQUFNQyxlQUFlLFNBQVNILFFBQVEsQ0FBQztFQUFBO0lBQUEsQ0FBQUksb0JBQUEsRUFBQUMsb0JBQUEsRUFBQUMsdUJBQUEsRUFBQUMscUJBQUEsRUFBQUMsa0JBQUEsRUFBQUMsdUJBQUEsRUFBQUMsc0JBQUEsRUFBQUMseUNBQUEsRUFBQUMsc0JBQUEsRUFBQUMscUJBQUEsRUFBQUMscUJBQUEsRUFBQUMsVUFBQSxJQUFBQyxVQUFBLFNBQUFDLG1CQUFBLHlCQUFBQyxtQkFBQSx5QkFBQUMsc0JBQUEsNEJBQUFDLG9CQUFBLDBCQUFBQyxpQkFBQSx1QkFBQUMsc0JBQUEsNEJBQUFDLHFCQUFBLDJCQUFBQyx3Q0FBQSw4Q0FBQUMscUJBQUEseUJBQUFDLENBQUEsSUFBQUEsQ0FBQSxFQUFBQyxDQUFBLEdBQUFELENBQUEsRUFBQUUsQ0FBQSxLQUFBRixDQUFBLEVBQUFDLENBQUEsR0FBQUMsQ0FBQSxXQUFBQyxDQUFBLElBMEI1QixDQUFDQyxnQkFBZ0IsSUFBQUQsQ0FBQSxFQTFCRTdCLFFBQVEsRUFBQStC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 \ No newline at end of file diff --git a/test-next/test-basic-properties.src.js b/test-next/test-basic-properties.src.js new file mode 100644 index 0000000..a435526 --- /dev/null +++ b/test-next/test-basic-properties.src.js @@ -0,0 +1,543 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +class TestElementNext extends XElement { + @property({ type: String, initial: 'Ferus' }) + accessor normalProperty; + + @property({ type: Object }) + accessor objectProperty; + + @property({ type: String, initial: 'Bactrian' }) + accessor camelCaseProperty; + + @property({ type: Number, default: 10 }) + accessor numericProperty; + + @property({ type: String, default: null }) + accessor nullProperty; + + @property({ type: String }) + accessor undefinedProperty; + + @property({}) + accessor typelessProperty; + + @property({ attribute: 'custom-attribute-typeless' }) + accessor typelessPropertyWithCustomAttribute; + + @property({}) + accessor #internalProperty; + + static template(host) { + return html` +
${host.normalProperty}
+ ${host.camelCaseProperty} + ${host.numericProperty} + ${host.nullProperty} + ${host.undefinedProperty} + `; + } +} +customElements.define('test-element-next', TestElementNext); + +it('initialization', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('normal').textContent === 'Ferus'); + assert(el.shadowRoot.getElementById('camel').textContent === 'Bactrian'); + assert(el.shadowRoot.getElementById('num').textContent === '10'); + assert(el.shadowRoot.getElementById('nul').textContent === ''); +}); + +it('renders an empty string in place of null value', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('nul').textContent === ''); +}); + +it('renders an empty string in place of undefined value', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.getElementById('undef').textContent === ''); +}); + +it('property setter updates on next micro tick after connect', async () => { + const el = document.createElement('test-element-next'); + el.camelCaseProperty = 'Nonconforming'; + + // Initial render happens synchronously after initial connection. + document.body.append(el); + assert(el.shadowRoot.getElementById('camel').textContent === 'Nonconforming'); + + // Updates are debounced on a microtask, so they are not immediately seen. + el.camelCaseProperty = 'Dromedary'; + assert(el.shadowRoot.getElementById('camel').textContent === 'Nonconforming'); + + // After the microtask runs, the update is handled. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Dromedary'); +}); + +it('property setter renders blank value', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.camelCaseProperty = ''; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === ''); + el.camelCaseProperty = 'Bactrian'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Bactrian'); +}); + +it('observes all dash-cased versions of public, typeless, serializable, and declared properties', () => { + const el = document.createElement('test-element-next'); + const expected = [ + 'normal-property', + 'camel-case-property', + 'numeric-property', + 'null-property', + 'undefined-property', + 'typeless-property', + 'custom-attribute-typeless', + ]; + const actual = el.constructor.observedAttributes; + assert(expected.length === actual.length); + assert(expected.every(attribute => actual.includes(attribute))); +}); + +it('removeAttribute renders blank', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.removeAttribute('camel-case-property'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + // Note, in general, changing non-reflected properties via attributes can + // be problematic. For example, attributeChangedCallback is not fired if the + // attribute does not change. + assert(el.shadowRoot.getElementById('camel').textContent !== ''); + el.setAttribute('camel-case-property', 'foo'); + el.removeAttribute('camel-case-property'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === ''); +}); + +it('setAttribute renders the new value', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.setAttribute('camel-case-property', 'Racing Camel'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('camel').textContent === 'Racing Camel'); +}); + +it('coerces attributes to the specified type', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.setAttribute('numeric-property', '-99'); + assert(el.numericProperty === -99); +}); + +it('allows properties without types', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + for (const value of [{}, 'foo', '5', [], 2]) { + el.typelessProperty = value; + assert(el.typelessProperty === value); + } +}); + +it('syncs attributes to properties without types', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const value = '123'; + el.setAttribute('typeless-property', value); + assert(el.typelessProperty === value); +}); + +it('initializes from attributes on connect', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('camel-case-property', 'Dromedary'); + document.body.append(el); + assert(el.camelCaseProperty === 'Dromedary'); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it('can use "has" api or "in" operator.', () => { + class TempTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + if (!('normalProperty' in host) || Reflect.has(host, 'normalProperty') === false) { + throw new Error('The "has" trap does not work.'); + } + return html``; + } + } + customElements.define('temp-test-element-next-1', TempTestElement); + const el = new TempTestElement(); + let passed = false; + let message; + try { + el.connectedCallback(); + passed = true; + } catch (error) { + message = error.message; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, ownKeys doesn’t +// work because properties are simply on the `host.prototype`. +it.skip('can use "ownKeys" api.', () => { + class TempTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + const ownKeys = Reflect.ownKeys(host); + if (ownKeys.length !== 1 || ownKeys[0] !== 'normalProperty') { + throw new Error('The "ownKeys" trap does not work.'); + } + return html``; + } + } + customElements.define('temp-test-element-next-2', TempTestElement); + const el = new TempTestElement(); + let passed = false; + let message; + try { + el.connectedCallback(); + passed = true; + } catch (error) { + message = error.message; + } + assert(passed, message); +}); + +it('can be read', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.normalProperty === 'Ferus', 'property was read'); + el.normalProperty = 'Dromedary'; + assert(el.normalProperty === 'Dromedary', 'property was written to'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.getElementById('normal').textContent === 'Dromedary'); +}); + +it('inheritance is considered in type checking', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const array = []; + el.objectProperty = array; + assert(el.objectProperty === array, 'property was set'); +}); + +it('numeric properties deserialize "" (empty) to "NaN"', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('numeric-property', '0'); + document.body.append(el); + assert(el.numericProperty === 0, '"0" was coerced to 0'); + el.setAttribute('numeric-property', ''); + assert(Number.isNaN(el.numericProperty), '"" was coerced to NaN'); + el.setAttribute('numeric-property', ' '); + assert(Number.isNaN(el.numericProperty), '" " was coerced to NaN'); + el.setAttribute('numeric-property', ' '); + assert(Number.isNaN(el.numericProperty), '" " was coerced to NaN'); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not update properties in the render loop. +it.skip('cannot set to known properties', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + host.normalProperty = 'Dromedary'; + return html`
${host.normalProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Cannot set "BadTestElement.properties.normalProperty" via "properties".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not update properties in the render loop. +it.skip('cannot set to unknown properties', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + host.doesNotExist = 'Dromedary'; + return html`
${host.normalProperty}
`; + } + } + customElements.define('bad-test-element-next-2', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. In this case, we expect our +// users to be experts and not pull objects which don’t exist (or understand +// that it doesn’t exist if it is returning “undefined”). +it.skip('cannot get unknown properties', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + return html`
${host.doesNotExist}
`; + } + } + customElements.define('bad-test-element-next-3', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test internal proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot set on "internal"', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.normalProperty = 'Dromedary'; + } catch (error) { + const expected = 'Property "TestElement.properties.normalProperty" is publicly available (use normal setter).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. You can of course define a +// new property on an instance like this. This would work even if it was truly +// shadowing an accessor that already exists. +it.skip('cannot "defineProperty" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.defineProperty(host, 'foo', {}); + return html``; + } + } + customElements.define('bad-test-element-next-4', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. You can of course call delete +// on an instance like this — but, this doesn’t break the accessors in any way. +it.skip('cannot "delete" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.deleteProperty(host, 'normalProperty'); + return html``; + } + } + customElements.define('bad-test-element-next-5', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "getOwnPropertyDescriptor" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.getOwnPropertyDescriptor(host, 'normalProperty'); + return html``; + } + } + customElements.define('bad-test-element-next-6', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "getPrototypeOf" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.getPrototypeOf(host); + return html``; + } + } + customElements.define('bad-test-element-next-7', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "isExtensible" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.isExtensible(host); + return html``; + } + } + customElements.define('bad-test-element-next-8', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "preventExtensions" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.preventExtensions(host); + return html``; + } + } + customElements.define('bad-test-element-next-9', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy, but it +// now just checks basic, idiomatic JavaScript. +it.skip('cannot "setPrototypeOf" on properties.', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor normalProperty; + + static template(host) { + Reflect.setPrototypeOf(host, Array); + return html``; + } + } + customElements.define('bad-test-element-next-10', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid use of properties proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-computed-properties.html b/test-next/test-computed-properties.html new file mode 100644 index 0000000..e94a85b --- /dev/null +++ b/test-next/test-computed-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Computed Properties

+ + + diff --git a/test-next/test-computed-properties.js b/test-next/test-computed-properties.js new file mode 100644 index 0000000..dd9e5ac --- /dev/null +++ b/test-next/test-computed-properties.js @@ -0,0 +1,382 @@ +let _initProto, _cDecs, _init_c, _get_c, _set_c, _cDecs2, _init_c2, _aDecs, _init_a, _bDecs, _init_b, _negativeDecs, _init_negative, _underlineDecs, _init_underline, _italicDecs, _init_italic, _yDecs, _init_y, _zDecs, _init_z, _todayDecs, _init_today, _tomorrowDecs, _init_tomorrow, _countTriggerDecs, _init_countTrigger, _countDecs, _init_count; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { it, assert } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +let _count = 0; +class TestElement extends XElement { + static { + [_init_c, _get_c, _set_c, _init_c2, _init_a, _init_b, _init_negative, _init_underline, _init_italic, _init_y, _init_z, _init_today, _init_tomorrow, _init_countTrigger, _init_count, _initProto] = _applyDecs(this, [[_cDecs, 1, "c", o => o.#A, (o, v) => o.#A = v], [_cDecs2, 1, "c"], [_aDecs, 1, "a"], [_bDecs, 1, "b"], [_negativeDecs, 1, "negative"], [_underlineDecs, 1, "underline"], [_italicDecs, 1, "italic"], [_yDecs, 1, "y"], [_zDecs, 1, "z"], [_todayDecs, 1, "today"], [_tomorrowDecs, 1, "tomorrow"], [_countTriggerDecs, 1, "countTrigger"], [_countDecs, 1, "count"]], [], 0, _ => #c in _, XElement).e; + } + static get [(_cDecs = property({ + type: Number, + input: ['a', 'b'], + compute: (a, b) => TestElement.#computeC(a, b) + }), _cDecs2 = property({ + type: Number, + input: ['#c'], + compute: c => c + }), _aDecs = property({ + type: Number + }), _bDecs = property({ + type: Number + }), _negativeDecs = property({ + type: Boolean, + input: ['#c'], + compute: c => c < 0, + reflect: true + }), _underlineDecs = property({ + type: Boolean, + input: ['negative'], + compute: negative => TestElement.#computeUnderline(negative), + reflect: true + }), _italicDecs = property({ + type: Boolean, + reflect: true + }), _yDecs = property({ + type: Boolean + }), _zDecs = property({ + type: Boolean, + input: ['y'], + compute: y => TestElement.#computeZ(y) + }), _todayDecs = property({ + type: Date + }), _tomorrowDecs = property({ + type: Date, + input: ['today'], + compute: today => TestElement.#computeTomorrow(today) + }), _countTriggerDecs = property({}), _countDecs = property({ + type: Number, + input: ['countTrigger'], + compute: () => TestElement.#computeCount() + }), "styles")]() { + const styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(`\ + #calculation { + background-color: lightgreen; + padding: 10px; + } + + :host([negative]) #calculation { + background-color: lightcoral; + } + + :host([underline]) #calculation { + text-decoration: underline; + } + + :host([italic]) #calculation { + font-style: italic; + } + `); + return [styleSheet]; + } + #A = (_initProto(this), _init_c(this)); + set #c(v) { + _set_c(this, v); + } + get #c() { + return _get_c(this); + } + #B = _init_c2(this); + get c() { + return this.#B; + } + set c(v) { + this.#B = v; + } + #C = _init_a(this); + get a() { + return this.#C; + } + set a(v) { + this.#C = v; + } + #D = _init_b(this); + get b() { + return this.#D; + } + set b(v) { + this.#D = v; + } + #E = _init_negative(this); + get negative() { + return this.#E; + } + set negative(v) { + this.#E = v; + } + #F = _init_underline(this); + get underline() { + return this.#F; + } + set underline(v) { + this.#F = v; + } + #G = _init_italic(this); + get italic() { + return this.#G; + } + set italic(v) { + this.#G = v; + } + #H = _init_y(this); + get y() { + return this.#H; + } + set y(v) { + this.#H = v; + } + #I = _init_z(this); + get z() { + return this.#I; + } + set z(v) { + this.#I = v; + } + #J = _init_today(this); + get today() { + return this.#J; + } + set today(v) { + this.#J = v; + } + #K = _init_tomorrow(this); + get tomorrow() { + return this.#K; + } + set tomorrow(v) { + this.#K = v; + } + #L = _init_countTrigger(this); + get countTrigger() { + return this.#L; + } + set countTrigger(v) { + this.#L = v; + } + #M = _init_count(this); + get count() { + return this.#M; + } + set count(v) { + this.#M = v; + } + static #computeC(a, b) { + return a + b; + } + static #computeCount() { + // This doesn't use an observer to prevent a coupled test. + return ++_count; + } + static #computeUnderline(negative) { + return !!negative; + } + static #computeZ(y) { + return y; + } + static #computeTomorrow(today) { + if (today) { + return today.valueOf() + 1000 * 60 * 60 * 24; + } + } + static template(host) { + return html`${host.a} + ${host.b} = ${host.#c}`; + } +} +customElements.define('test-element-next', TestElement); +it('initializes as expected', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.a === undefined); + assert(el.b === undefined); + assert(el.y === undefined); + assert(el.z === undefined); + assert(el.countTrigger === undefined); + assert(Number.isNaN(el.c)); + assert(el.negative === false); + assert(el.underline === false); +}); +it('properties are recomputed when dependencies change (a, b)', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.a = 1; + el.b = -2; + assert(el.a === 1); + assert(el.b === -2); + assert(el.c === -1); + assert(el.negative === true); + assert(el.underline === true); +}); +it('properties are recomputed when dependencies change (y)', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.y = true; + assert(el.y === true); + assert(el.z === true); + el.y = false; + assert(el.y === false); + assert(el.z === false); +}); +it('computed properties can be reflected', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.a = -1; + el.b = 0; + assert(el.c === -1); + assert(el.negative === true); + assert(el.underline === true); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.hasAttribute('negative')); + assert(el.hasAttribute('underline')); +}); +it('skips resolution when dependencies are the same', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = 'foo'; + assert(el.count === ++count); + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + assert(el.count === count); + el.countTrigger = 'bar'; + assert(el.count === ++count); +}); +it('lazily computes', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = 'foo'; + assert(el.count === ++count); + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + assert(el.count === count); + el.countTrigger = 'bar'; + assert(el.count === ++count); +}); +it('does correct NaN checking', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = NaN; + assert(el.count === ++count); + el.countTrigger = NaN; + assert(el.count === count); +}); +it('resets compute validity on initialization to catch upgrade edge cases with computed properties', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('a', '1'); + el.setAttribute('b', '2'); + assert(el.a === undefined); + assert(el.b === undefined); + assert(el.c === undefined); + document.body.append(el); + assert(el.a === 1); + assert(el.b === 2); + assert(el.c === 3); +}); +it('cannot be written to from host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.count = 0; + } catch (error) { + const expected = 'Property "TestElement.prototype.count" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('cannot set to known properties', () => { + let _initProto2, _computedPropertyDecs, _init_computedProperty; + class BadTestElement extends XElement { + static { + [_init_computedProperty, _initProto2] = _applyDecs(this, [[_computedPropertyDecs, 1, "computedProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_computedProperty(this)); + get [(_computedPropertyDecs = property({ + type: String, + input: [], + compute: () => {} + }), "computedProperty")]() { + return this.#A; + } + set computedProperty(v) { + this.#A = v; + } + static template(host) { + host.computedProperty = 'Dromedary'; + return html`
${host.computedProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.prototype.computedProperty" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('cannot compute a bad value', () => { + let _initProto3, _computedPropertyDecs2, _init_computedProperty2; + class BadTestElement extends XElement { + static { + [_init_computedProperty2, _initProto3] = _applyDecs(this, [[_computedPropertyDecs2, 1, "computedProperty"]], [], 0, void 0, XElement).e; + } + #A = (_initProto3(this), _init_computedProperty2(this)); + get [(_computedPropertyDecs2 = property({ + type: String, + input: [], + compute: () => 0 + }), "computedProperty")]() { + return this.#A; + } + set computedProperty(v) { + this.#A = v; + } + static template(host) { + return html`${host.computedProperty}`; + } + } + customElements.define('bad-test-element-next-2', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.computedProperty" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJpdCIsImFzc2VydCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiX2NvdW50IiwiVGVzdEVsZW1lbnQiLCJfaW5pdF9jIiwiX2dldF9jIiwiX3NldF9jIiwiX2luaXRfYzIiLCJfaW5pdF9hIiwiX2luaXRfYiIsIl9pbml0X25lZ2F0aXZlIiwiX2luaXRfdW5kZXJsaW5lIiwiX2luaXRfaXRhbGljIiwiX2luaXRfeSIsIl9pbml0X3oiLCJfaW5pdF90b2RheSIsIl9pbml0X3RvbW9ycm93IiwiX2luaXRfY291bnRUcmlnZ2VyIiwiX2luaXRfY291bnQiLCJfaW5pdFByb3RvIiwiX2FwcGx5RGVjcyIsIl9jRGVjcyIsIm8iLCJBIiwidiIsIl9jRGVjczIiLCJfYURlY3MiLCJfYkRlY3MiLCJfbmVnYXRpdmVEZWNzIiwiX3VuZGVybGluZURlY3MiLCJfaXRhbGljRGVjcyIsIl95RGVjcyIsIl96RGVjcyIsIl90b2RheURlY3MiLCJfdG9tb3Jyb3dEZWNzIiwiX2NvdW50VHJpZ2dlckRlY3MiLCJfY291bnREZWNzIiwiXyIsImMiLCJlIiwidHlwZSIsIk51bWJlciIsImlucHV0IiwiY29tcHV0ZSIsImEiLCJiIiwiY29tcHV0ZUMiLCJCb29sZWFuIiwicmVmbGVjdCIsIm5lZ2F0aXZlIiwiY29tcHV0ZVVuZGVybGluZSIsInkiLCJjb21wdXRlWiIsIkRhdGUiLCJ0b2RheSIsImNvbXB1dGVUb21vcnJvdyIsImNvbXB1dGVDb3VudCIsInN0eWxlU2hlZXQiLCJDU1NTdHlsZVNoZWV0IiwicmVwbGFjZVN5bmMiLCIjYyIsIkIiLCJDIiwiRCIsIkUiLCJGIiwidW5kZXJsaW5lIiwiRyIsIml0YWxpYyIsIkgiLCJJIiwieiIsIkoiLCJLIiwidG9tb3Jyb3ciLCJMIiwiY291bnRUcmlnZ2VyIiwiTSIsImNvdW50IiwiI2NvbXB1dGVDIiwiI2NvbXB1dGVDb3VudCIsIiNjb21wdXRlVW5kZXJsaW5lIiwiI2NvbXB1dGVaIiwiI2NvbXB1dGVUb21vcnJvdyIsInZhbHVlT2YiLCJ0ZW1wbGF0ZSIsImhvc3QiLCJjdXN0b21FbGVtZW50cyIsImRlZmluZSIsImVsIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwiYm9keSIsImFwcGVuZCIsInVuZGVmaW5lZCIsImlzTmFOIiwiUHJvbWlzZSIsInJlc29sdmUiLCJoYXNBdHRyaWJ1dGUiLCJOYU4iLCJzZXRBdHRyaWJ1dGUiLCJwYXNzZWQiLCJtZXNzYWdlIiwiZXJyb3IiLCJleHBlY3RlZCIsIl9pbml0UHJvdG8yIiwiX2NvbXB1dGVkUHJvcGVydHlEZWNzIiwiX2luaXRfY29tcHV0ZWRQcm9wZXJ0eSIsIkJhZFRlc3RFbGVtZW50IiwiU3RyaW5nIiwiY29tcHV0ZWRQcm9wZXJ0eSIsImNvbm5lY3RlZENhbGxiYWNrIiwiX2luaXRQcm90bzMiLCJfY29tcHV0ZWRQcm9wZXJ0eURlY3MyIiwiX2luaXRfY29tcHV0ZWRQcm9wZXJ0eTIiXSwic291cmNlcyI6WyJ0ZXN0LWNvbXB1dGVkLXByb3BlcnRpZXMuc3JjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGl0LCBhc3NlcnQgfSBmcm9tICdAbmV0ZmxpeC94LXRlc3QveC10ZXN0LmpzJztcbmltcG9ydCB7IFhFbGVtZW50LCBwcm9wZXJ0eSwgaHRtbCB9IGZyb20gJy4uL3gtZWxlbWVudC1uZXh0LmpzJztcblxubGV0IF9jb3VudCA9IDA7XG5cbmNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICBzdGF0aWMgZ2V0IHN0eWxlcygpIHtcbiAgICBjb25zdCBzdHlsZVNoZWV0ID0gbmV3IENTU1N0eWxlU2hlZXQoKTtcbiAgICBzdHlsZVNoZWV0LnJlcGxhY2VTeW5jKGBcXFxuICAgICAgI2NhbGN1bGF0aW9uIHtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmVlbjtcbiAgICAgICAgcGFkZGluZzogMTBweDtcbiAgICAgIH1cblxuICAgICAgOmhvc3QoW25lZ2F0aXZlXSkgI2NhbGN1bGF0aW9uIHtcbiAgICAgICAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRjb3JhbDtcbiAgICAgIH1cblxuICAgICAgOmhvc3QoW3VuZGVybGluZV0pICNjYWxjdWxhdGlvbiB7XG4gICAgICAgIHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lO1xuICAgICAgfVxuXG4gICAgICA6aG9zdChbaXRhbGljXSkgI2NhbGN1bGF0aW9uIHtcbiAgICAgICAgZm9udC1zdHlsZTogaXRhbGljO1xuICAgICAgfVxuICAgIGApO1xuICAgIHJldHVybiBbc3R5bGVTaGVldF07XG4gIH1cblxuICBAcHJvcGVydHkoe1xuICAgIHR5cGU6IE51bWJlcixcbiAgICBpbnB1dDogWydhJywgJ2InXSxcbiAgICBjb21wdXRlOiAoYSwgYikgPT4gVGVzdEVsZW1lbnQuI2NvbXB1dGVDKGEsIGIpLFxuICB9KVxuICBhY2Nlc3NvciAjYztcblxuICBAcHJvcGVydHkoe1xuICAgIHR5cGU6IE51bWJlcixcbiAgICBpbnB1dDogWycjYyddLFxuICAgIGNvbXB1dGU6IChjKSA9PiBjLFxuICB9KVxuICBhY2Nlc3NvciBjO1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciB9KVxuICBhY2Nlc3NvciBhO1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciB9KVxuICBhY2Nlc3NvciBiO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogQm9vbGVhbixcbiAgICBpbnB1dDogWycjYyddLFxuICAgIGNvbXB1dGU6IGMgPT4gYyA8IDAsXG4gICAgcmVmbGVjdDogdHJ1ZSxcbiAgfSlcbiAgYWNjZXNzb3IgbmVnYXRpdmU7XG5cbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBCb29sZWFuLFxuICAgIGlucHV0OiBbJ25lZ2F0aXZlJ10sXG4gICAgY29tcHV0ZTogKG5lZ2F0aXZlKSA9PiBUZXN0RWxlbWVudC4jY29tcHV0ZVVuZGVybGluZShuZWdhdGl2ZSksXG4gICAgcmVmbGVjdDogdHJ1ZSxcbiAgfSlcbiAgYWNjZXNzb3IgdW5kZXJsaW5lO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogQm9vbGVhbixcbiAgICByZWZsZWN0OiB0cnVlLFxuICB9KVxuICBhY2Nlc3NvciBpdGFsaWM7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogQm9vbGVhbiB9KVxuICBhY2Nlc3NvciB5O1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogQm9vbGVhbixcbiAgICBpbnB1dDogWyd5J10sXG4gICAgY29tcHV0ZTogKHkpID0+IFRlc3RFbGVtZW50LiNjb21wdXRlWih5KSxcbiAgfSlcbiAgYWNjZXNzb3IgejtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBEYXRlIH0pXG4gIGFjY2Vzc29yIHRvZGF5O1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogRGF0ZSxcbiAgICBpbnB1dDogWyd0b2RheSddLFxuICAgIGNvbXB1dGU6ICh0b2RheSkgPT4gVGVzdEVsZW1lbnQuI2NvbXB1dGVUb21vcnJvdyh0b2RheSksXG4gIH0pXG4gIGFjY2Vzc29yIHRvbW9ycm93O1xuXG4gIEBwcm9wZXJ0eSh7fSlcbiAgYWNjZXNzb3IgY291bnRUcmlnZ2VyO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogTnVtYmVyLFxuICAgIGlucHV0OiBbJ2NvdW50VHJpZ2dlciddLFxuICAgIGNvbXB1dGU6ICgpID0+IFRlc3RFbGVtZW50LiNjb21wdXRlQ291bnQoKSxcbiAgfSlcbiAgYWNjZXNzb3IgY291bnQ7XG5cbiAgc3RhdGljICNjb21wdXRlQyhhLCBiKSB7XG4gICAgcmV0dXJuIGEgKyBiO1xuICB9XG5cbiAgc3RhdGljICNjb21wdXRlQ291bnQoKSB7XG4gICAgLy8gVGhpcyBkb2Vzbid0IHVzZSBhbiBvYnNlcnZlciB0byBwcmV2ZW50IGEgY291cGxlZCB0ZXN0LlxuICAgIHJldHVybiArK19jb3VudDtcbiAgfVxuXG4gIHN0YXRpYyAjY29tcHV0ZVVuZGVybGluZShuZWdhdGl2ZSkge1xuICAgIHJldHVybiAhIW5lZ2F0aXZlO1xuICB9XG5cbiAgc3RhdGljICNjb21wdXRlWih5KSB7XG4gICAgcmV0dXJuIHk7XG4gIH1cblxuICBzdGF0aWMgI2NvbXB1dGVUb21vcnJvdyh0b2RheSkge1xuICAgIGlmICh0b2RheSkge1xuICAgICAgcmV0dXJuIHRvZGF5LnZhbHVlT2YoKSArIDEwMDAgKiA2MCAqIDYwICogMjQ7XG4gICAgfVxuICB9XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGA8c3BhbiBpZD1cImNhbGN1bGF0aW9uXCI+JHtob3N0LmF9ICsgJHtob3N0LmJ9ID0gJHtob3N0LiNjfTwvc3Bhbj5gO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1uZXh0JywgVGVzdEVsZW1lbnQpO1xuXG5pdCgnaW5pdGlhbGl6ZXMgYXMgZXhwZWN0ZWQnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuYSA9PT0gdW5kZWZpbmVkKTtcbiAgYXNzZXJ0KGVsLmIgPT09IHVuZGVmaW5lZCk7XG4gIGFzc2VydChlbC55ID09PSB1bmRlZmluZWQpO1xuICBhc3NlcnQoZWwueiA9PT0gdW5kZWZpbmVkKTtcbiAgYXNzZXJ0KGVsLmNvdW50VHJpZ2dlciA9PT0gdW5kZWZpbmVkKTtcbiAgYXNzZXJ0KE51bWJlci5pc05hTihlbC5jKSk7XG4gIGFzc2VydChlbC5uZWdhdGl2ZSA9PT0gZmFsc2UpO1xuICBhc3NlcnQoZWwudW5kZXJsaW5lID09PSBmYWxzZSk7XG59KTtcblxuaXQoJ3Byb3BlcnRpZXMgYXJlIHJlY29tcHV0ZWQgd2hlbiBkZXBlbmRlbmNpZXMgY2hhbmdlIChhLCBiKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGVsLmEgPSAxO1xuICBlbC5iID0gLTI7XG4gIGFzc2VydChlbC5hID09PSAxKTtcbiAgYXNzZXJ0KGVsLmIgPT09IC0yKTtcbiAgYXNzZXJ0KGVsLmMgPT09IC0xKTtcbiAgYXNzZXJ0KGVsLm5lZ2F0aXZlID09PSB0cnVlKTtcbiAgYXNzZXJ0KGVsLnVuZGVybGluZSA9PT0gdHJ1ZSk7XG59KTtcblxuaXQoJ3Byb3BlcnRpZXMgYXJlIHJlY29tcHV0ZWQgd2hlbiBkZXBlbmRlbmNpZXMgY2hhbmdlICh5KScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGVsLnkgPSB0cnVlO1xuICBhc3NlcnQoZWwueSA9PT0gdHJ1ZSk7XG4gIGFzc2VydChlbC56ID09PSB0cnVlKTtcbiAgZWwueSA9IGZhbHNlO1xuICBhc3NlcnQoZWwueSA9PT0gZmFsc2UpO1xuICBhc3NlcnQoZWwueiA9PT0gZmFsc2UpO1xufSk7XG5cbml0KCdjb21wdXRlZCBwcm9wZXJ0aWVzIGNhbiBiZSByZWZsZWN0ZWQnLCBhc3luYyAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBlbC5hID0gLTE7XG4gIGVsLmIgPSAwO1xuICBhc3NlcnQoZWwuYyA9PT0gLTEpO1xuICBhc3NlcnQoZWwubmVnYXRpdmUgPT09IHRydWUpO1xuICBhc3NlcnQoZWwudW5kZXJsaW5lID09PSB0cnVlKTtcblxuICAvLyBXZSBtdXN0IGF3YWl0IGEgbWljcm90YXNrIGZvciB0aGUgdXBkYXRlIHRvIHRha2UgcGxhY2UuXG4gIGF3YWl0IFByb21pc2UucmVzb2x2ZSgpO1xuICBhc3NlcnQoZWwuaGFzQXR0cmlidXRlKCduZWdhdGl2ZScpKTtcbiAgYXNzZXJ0KGVsLmhhc0F0dHJpYnV0ZSgndW5kZXJsaW5lJykpO1xufSk7XG5cbml0KCdza2lwcyByZXNvbHV0aW9uIHdoZW4gZGVwZW5kZW5jaWVzIGFyZSB0aGUgc2FtZScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBjb3VudCA9IGVsLmNvdW50O1xuICBlbC5jb3VudFRyaWdnZXIgPSAnZm9vJztcbiAgYXNzZXJ0KGVsLmNvdW50ID09PSArK2NvdW50KTtcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Zvbyc7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdmb28nO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnZm9vJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Zvbyc7XG4gIGFzc2VydChlbC5jb3VudCA9PT0gY291bnQpO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnYmFyJztcbiAgYXNzZXJ0KGVsLmNvdW50ID09PSArK2NvdW50KTtcbn0pO1xuXG5pdCgnbGF6aWx5IGNvbXB1dGVzJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IGNvdW50ID0gZWwuY291bnQ7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdmb28nO1xuICBhc3NlcnQoZWwuY291bnQgPT09ICsrY291bnQpO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnYmFyJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Zvbyc7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdiYXInO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnZm9vJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Jhcic7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdmb28nO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnYmFyJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Zvbyc7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdiYXInO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnZm9vJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Jhcic7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdmb28nO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnYmFyJztcbiAgZWwuY291bnRUcmlnZ2VyID0gJ2Zvbyc7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdiYXInO1xuICBlbC5jb3VudFRyaWdnZXIgPSAnZm9vJztcbiAgYXNzZXJ0KGVsLmNvdW50ID09PSBjb3VudCk7XG4gIGVsLmNvdW50VHJpZ2dlciA9ICdiYXInO1xuICBhc3NlcnQoZWwuY291bnQgPT09ICsrY291bnQpO1xufSk7XG5cbml0KCdkb2VzIGNvcnJlY3QgTmFOIGNoZWNraW5nJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IGNvdW50ID0gZWwuY291bnQ7XG4gIGVsLmNvdW50VHJpZ2dlciA9IE5hTjtcbiAgYXNzZXJ0KGVsLmNvdW50ID09PSArK2NvdW50KTtcbiAgZWwuY291bnRUcmlnZ2VyID0gTmFOO1xuICBhc3NlcnQoZWwuY291bnQgPT09IGNvdW50KTtcbn0pO1xuXG5pdCgncmVzZXRzIGNvbXB1dGUgdmFsaWRpdHkgb24gaW5pdGlhbGl6YXRpb24gdG8gY2F0Y2ggdXBncmFkZSBlZGdlIGNhc2VzIHdpdGggY29tcHV0ZWQgcHJvcGVydGllcycsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ2EnLCAnMScpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ2InLCAnMicpO1xuICBhc3NlcnQoZWwuYSA9PT0gdW5kZWZpbmVkKTtcbiAgYXNzZXJ0KGVsLmIgPT09IHVuZGVmaW5lZCk7XG4gIGFzc2VydChlbC5jID09PSB1bmRlZmluZWQpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5hID09PSAxKTtcbiAgYXNzZXJ0KGVsLmIgPT09IDIpO1xuICBhc3NlcnQoZWwuYyA9PT0gMyk7XG59KTtcblxuaXQoJ2Nhbm5vdCBiZSB3cml0dGVuIHRvIGZyb20gaG9zdCcsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY291bnQgPSAwO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Byb3BlcnR5IFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLmNvdW50XCIgaXMgY29tcHV0ZWQgKGNvbXB1dGVkIHByb3BlcnRpZXMgYXJlIHJlYWQtb25seSkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgnY2Fubm90IHNldCB0byBrbm93biBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoe1xuICAgICAgdHlwZTogU3RyaW5nLFxuICAgICAgaW5wdXQ6IFtdLFxuICAgICAgY29tcHV0ZTogKCkgPT4ge30sXG4gICAgfSlcbiAgICBhY2Nlc3NvciBjb21wdXRlZFByb3BlcnR5O1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIGhvc3QuY29tcHV0ZWRQcm9wZXJ0eSA9ICdEcm9tZWRhcnknO1xuICAgICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3QuY29tcHV0ZWRQcm9wZXJ0eX08L2Rpdj5gO1xuICAgIH1cbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ2JhZC10ZXN0LWVsZW1lbnQtbmV4dC0xJywgQmFkVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBCYWRUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmNvbm5lY3RlZENhbGxiYWNrKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnUHJvcGVydHkgXCJCYWRUZXN0RWxlbWVudC5wcm90b3R5cGUuY29tcHV0ZWRQcm9wZXJ0eVwiIGlzIGNvbXB1dGVkIChjb21wdXRlZCBwcm9wZXJ0aWVzIGFyZSByZWFkLW9ubHkpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2Nhbm5vdCBjb21wdXRlIGEgYmFkIHZhbHVlJywgKCkgPT4ge1xuICBjbGFzcyBCYWRUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoe1xuICAgICAgdHlwZTogU3RyaW5nLFxuICAgICAgaW5wdXQ6IFtdLFxuICAgICAgY29tcHV0ZTogKCkgPT4gMCxcbiAgICB9KVxuICAgIGFjY2Vzc29yIGNvbXB1dGVkUHJvcGVydHk7XG5cbiAgICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgICAgcmV0dXJuIGh0bWxgJHtob3N0LmNvbXB1dGVkUHJvcGVydHl9YDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCdiYWQtdGVzdC1lbGVtZW50LW5leHQtMicsIEJhZFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgQmFkVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQgdmFsdWUgZm9yIFwiQmFkVGVzdEVsZW1lbnQucHJvdG90eXBlLmNvbXB1dGVkUHJvcGVydHlcIiAoZXhwZWN0ZWQgU3RyaW5nLCBnb3QgTnVtYmVyKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLFNBQVNBLEVBQUUsRUFBRUMsTUFBTSxRQUFRLDJCQUEyQjtBQUN0RCxTQUFTQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsSUFBSSxRQUFRLHNCQUFzQjtBQUUvRCxJQUFJQyxNQUFNLEdBQUcsQ0FBQztBQUVkLE1BQU1DLFdBQVcsU0FBU0osUUFBUSxDQUFDO0VBQUE7SUFBQSxDQUFBSyxPQUFBLEVBQUFDLE1BQUEsRUFBQUMsTUFBQSxFQUFBQyxRQUFBLEVBQUFDLE9BQUEsRUFBQUMsT0FBQSxFQUFBQyxjQUFBLEVBQUFDLGVBQUEsRUFBQUMsWUFBQSxFQUFBQyxPQUFBLEVBQUFDLE9BQUEsRUFBQUMsV0FBQSxFQUFBQyxjQUFBLEVBQUFDLGtCQUFBLEVBQUFDLFdBQUEsRUFBQUMsVUFBQSxJQUFBQyxVQUFBLFNBQUFDLE1BQUEsVUFBQUMsQ0FBQSxJQUFBQSxDQUFBLEVBQUFDLENBQUEsR0FBQUQsQ0FBQSxFQUFBRSxDQUFBLEtBQUFGLENBQUEsRUFBQUMsQ0FBQSxHQUFBQyxDQUFBLElBQUFDLE9BQUEsWUFBQUMsTUFBQSxZQUFBQyxNQUFBLFlBQUFDLGFBQUEsbUJBQUFDLGNBQUEsb0JBQUFDLFdBQUEsaUJB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 \ No newline at end of file diff --git a/test-next/test-computed-properties.src.js b/test-next/test-computed-properties.src.js new file mode 100644 index 0000000..d1e89fe --- /dev/null +++ b/test-next/test-computed-properties.src.js @@ -0,0 +1,315 @@ +import { it, assert } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +let _count = 0; + +class TestElement extends XElement { + static get styles() { + const styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(`\ + #calculation { + background-color: lightgreen; + padding: 10px; + } + + :host([negative]) #calculation { + background-color: lightcoral; + } + + :host([underline]) #calculation { + text-decoration: underline; + } + + :host([italic]) #calculation { + font-style: italic; + } + `); + return [styleSheet]; + } + + @property({ + type: Number, + input: ['a', 'b'], + compute: (a, b) => TestElement.#computeC(a, b), + }) + accessor #c; + + @property({ + type: Number, + input: ['#c'], + compute: (c) => c, + }) + accessor c; + + @property({ type: Number }) + accessor a; + + @property({ type: Number }) + accessor b; + + @property({ + type: Boolean, + input: ['#c'], + compute: c => c < 0, + reflect: true, + }) + accessor negative; + + @property({ + type: Boolean, + input: ['negative'], + compute: (negative) => TestElement.#computeUnderline(negative), + reflect: true, + }) + accessor underline; + + @property({ + type: Boolean, + reflect: true, + }) + accessor italic; + + @property({ type: Boolean }) + accessor y; + + @property({ + type: Boolean, + input: ['y'], + compute: (y) => TestElement.#computeZ(y), + }) + accessor z; + + @property({ type: Date }) + accessor today; + + @property({ + type: Date, + input: ['today'], + compute: (today) => TestElement.#computeTomorrow(today), + }) + accessor tomorrow; + + @property({}) + accessor countTrigger; + + @property({ + type: Number, + input: ['countTrigger'], + compute: () => TestElement.#computeCount(), + }) + accessor count; + + static #computeC(a, b) { + return a + b; + } + + static #computeCount() { + // This doesn't use an observer to prevent a coupled test. + return ++_count; + } + + static #computeUnderline(negative) { + return !!negative; + } + + static #computeZ(y) { + return y; + } + + static #computeTomorrow(today) { + if (today) { + return today.valueOf() + 1000 * 60 * 60 * 24; + } + } + + static template(host) { + return html`${host.a} + ${host.b} = ${host.#c}`; + } +} +customElements.define('test-element-next', TestElement); + +it('initializes as expected', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.a === undefined); + assert(el.b === undefined); + assert(el.y === undefined); + assert(el.z === undefined); + assert(el.countTrigger === undefined); + assert(Number.isNaN(el.c)); + assert(el.negative === false); + assert(el.underline === false); +}); + +it('properties are recomputed when dependencies change (a, b)', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.a = 1; + el.b = -2; + assert(el.a === 1); + assert(el.b === -2); + assert(el.c === -1); + assert(el.negative === true); + assert(el.underline === true); +}); + +it('properties are recomputed when dependencies change (y)', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.y = true; + assert(el.y === true); + assert(el.z === true); + el.y = false; + assert(el.y === false); + assert(el.z === false); +}); + +it('computed properties can be reflected', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.a = -1; + el.b = 0; + assert(el.c === -1); + assert(el.negative === true); + assert(el.underline === true); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.hasAttribute('negative')); + assert(el.hasAttribute('underline')); +}); + +it('skips resolution when dependencies are the same', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = 'foo'; + assert(el.count === ++count); + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + el.countTrigger = 'foo'; + assert(el.count === count); + el.countTrigger = 'bar'; + assert(el.count === ++count); +}); + +it('lazily computes', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = 'foo'; + assert(el.count === ++count); + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + el.countTrigger = 'bar'; + el.countTrigger = 'foo'; + assert(el.count === count); + el.countTrigger = 'bar'; + assert(el.count === ++count); +}); + +it('does correct NaN checking', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = el.count; + el.countTrigger = NaN; + assert(el.count === ++count); + el.countTrigger = NaN; + assert(el.count === count); +}); + +it('resets compute validity on initialization to catch upgrade edge cases with computed properties', () => { + const el = document.createElement('test-element-next'); + el.setAttribute('a', '1'); + el.setAttribute('b', '2'); + assert(el.a === undefined); + assert(el.b === undefined); + assert(el.c === undefined); + document.body.append(el); + assert(el.a === 1); + assert(el.b === 2); + assert(el.c === 3); +}); + +it('cannot be written to from host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.count = 0; + } catch (error) { + const expected = 'Property "TestElement.prototype.count" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('cannot set to known properties', () => { + class BadTestElement extends XElement { + @property({ + type: String, + input: [], + compute: () => {}, + }) + accessor computedProperty; + + static template(host) { + host.computedProperty = 'Dromedary'; + return html`
${host.computedProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "BadTestElement.prototype.computedProperty" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('cannot compute a bad value', () => { + class BadTestElement extends XElement { + @property({ + type: String, + input: [], + compute: () => 0, + }) + accessor computedProperty; + + static template(host) { + return html`${host.computedProperty}`; + } + } + customElements.define('bad-test-element-next-2', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.computedProperty" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-default-properties.html b/test-next/test-default-properties.html new file mode 100644 index 0000000..c666d0b --- /dev/null +++ b/test-next/test-default-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Default Properties

+ + + diff --git a/test-next/test-default-properties.js b/test-next/test-default-properties.js new file mode 100644 index 0000000..8850ba5 --- /dev/null +++ b/test-next/test-default-properties.js @@ -0,0 +1,289 @@ +let _initProto, _oneDecs, _init_one, _initProto2, _oneDecs2, _init_one2, _twoDecs, _init_two, _initProto3, _oneDecs3, _init_one3, _twoDecs2, _init_two2, _initProto4, _oneDecs4, _init_one4, _twoDecs3, _init_two3, _threeDecs, _init_three; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +class TestElementBasic extends XElement { + static { + [_init_one, _initProto] = _applyDecs(this, [[_oneDecs, 1, "one"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_one(this)); + get [(_oneDecs = property({ + type: String, + default: 'one' + }), "one")]() { + return this.#A; + } + set one(v) { + this.#A = v; + } + static template(host) { + return html`
${host.one}
`; + } +} +customElements.define('test-element-basic-next', TestElementBasic); +class TestElementAnonymous extends XElement { + static { + [_init_one2, _init_two, _initProto2] = _applyDecs(this, [[_oneDecs2, 1, "one"], [_twoDecs, 1, "two"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_one2(this)); + get [(_oneDecs2 = property({ + type: String, + default: (() => 'one')() + }), _twoDecs = property({ + type: Object, + default: () => ({ + two: 'two' + }) + }), "one")]() { + return this.#A; + } + set one(v) { + this.#A = v; + } + #B = _init_two(this); + get two() { + return this.#B; + } + set two(v) { + this.#B = v; + } + static template(host) { + return html`
${host.one}, ${host.two.two}
`; + } +} +customElements.define('test-element-anonymous-next', TestElementAnonymous); +class TestElementStatic extends XElement { + static { + [_init_one3, _init_two2, _initProto3] = _applyDecs(this, [[_oneDecs3, 1, "one"], [_twoDecs2, 1, "two"]], [], 0, void 0, XElement).e; + } + static get [(_oneDecs3 = property({ + type: String, + default: () => TestElementStatic.getOne() + }), _twoDecs2 = property({ + type: Object, + default: () => TestElementStatic.getTwo() + }), "one")]() { + return 'one'; + } + static getOne() { + return this.one; + } + static get two() { + return { + two: 'two' + }; + } + static getTwo() { + return this.two; + } + #A = (_initProto3(this), _init_one3(this)); + get one() { + return this.#A; + } + set one(v) { + this.#A = v; + } + #B = _init_two2(this); + get two() { + return this.#B; + } + set two(v) { + this.#B = v; + } + static template(host) { + return html`
${host.one}, ${host.two.two}
`; + } +} +customElements.define('test-element-static-next', TestElementStatic); +class TestElementEdge extends XElement { + static { + [_init_one4, _init_two3, _init_three, _initProto4] = _applyDecs(this, [[_oneDecs4, 1, "one"], [_twoDecs3, 1, "two"], [_threeDecs, 1, "three"]], [], 0, void 0, XElement).e; + } + #A = (_initProto4(this), _init_one4(this)); + get [(_oneDecs4 = property({ + type: String, + initial: undefined, + default: 'one' + }), _twoDecs3 = property({ + type: Object, + initial: null, + default: () => ({ + two: 'two' + }) + }), _threeDecs = property({ + type: String, + input: [], + compute: () => {}, + default: 'three' + }), "one")]() { + return this.#A; + } + set one(v) { + this.#A = v; + } + #B = _init_two3(this); + get two() { + return this.#B; + } + set two(v) { + this.#B = v; + } + #C = _init_three(this); + get three() { + return this.#C; + } + set three(v) { + this.#C = v; + } + static template(host) { + return html`
${host.one}, ${host.two.two}, ${host.three}
`; + } +} +customElements.define('test-element-edge-next', TestElementEdge); +it('basic default properties', async () => { + const el = document.createElement('test-element-basic-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); + + // Test that we can override the default. + el.one = 'ONE'; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'ONE'); + + // Test that we get our default back via undefined. + el.one = undefined; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one'); + + // Override again... + el.one = 'ONE'; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'ONE'); + + // Test that we get our default back via null. + el.one = null; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic default properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic default properties (predefined null properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic default properties (predefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = 'ONE'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); +it('basic default properties (predefined attributes)', () => { + const el = document.createElement('test-element-basic-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); +it('anonymous default properties', () => { + const el = document.createElement('test-element-anonymous-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous default properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = undefined; + el.two = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous default properties (predefined null properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = null; + el.two = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous default properties (predefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = 'ONE'; + el.two = { + two: 'TWO' + }; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); +it('anonymous default properties (predefined attributes)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, two'); +}); +it('initial + default & computed + default properties', () => { + const el = document.createElement('test-element-edge-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two, three'); +}); +it('default values from functions are unique per instance', () => { + const el1 = document.createElement('test-element-edge-next'); + const el2 = document.createElement('test-element-edge-next'); + document.body.append(el1, el2); + assert(el1.shadowRoot.textContent === 'one, two, three'); + assert(el2.shadowRoot.textContent === 'one, two, three'); + assert(el1.two !== el2.two); +}); +it('default values from functions persist per instance', async () => { + const el = document.createElement('test-element-edge-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two, three'); + const defaultTwo = el.two; + el.two = { + two: 'TWO' + }; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one, TWO, three'); + assert(el.two !== defaultTwo); + el.two = undefined; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one, two, three'); + assert(el.two === defaultTwo); +}); +it('cannot set default to a bad type', () => { + let _initProto5, _badDecs, _init_bad; + class BadTestElement extends XElement { + static { + [_init_bad, _initProto5] = _applyDecs(this, [[_badDecs, 1, "bad"]], [], 0, void 0, XElement).e; + } + #A = (_initProto5(this), _init_bad(this)); + get [(_badDecs = property({ + type: String, + default: 0 + }), "bad")]() { + return this.#A; + } + set bad(v) { + this.#A = v; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.bad" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnRCYXNpYyIsIl9pbml0X29uZSIsIl9pbml0UHJvdG8iLCJfYXBwbHlEZWNzIiwiX29uZURlY3MiLCJlIiwiQSIsInR5cGUiLCJTdHJpbmciLCJkZWZhdWx0Iiwib25lIiwidiIsInRlbXBsYXRlIiwiaG9zdCIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiVGVzdEVsZW1lbnRBbm9ueW1vdXMiLCJfaW5pdF9vbmUyIiwiX2luaXRfdHdvIiwiX2luaXRQcm90bzIiLCJfb25lRGVjczIiLCJfdHdvRGVjcyIsIk9iamVjdCIsInR3byIsIkIiLCJUZXN0RWxlbWVudFN0YXRpYyIsIl9pbml0X29uZTMiLCJfaW5pdF90d28yIiwiX2luaXRQcm90bzMiLCJfb25lRGVjczMiLCJfdHdvRGVjczIiLCJnZXRPbmUiLCJnZXRUd28iLCJUZXN0RWxlbWVudEVkZ2UiLCJfaW5pdF9vbmU0IiwiX2luaXRfdHdvMyIsIl9pbml0X3RocmVlIiwiX2luaXRQcm90bzQiLCJfb25lRGVjczQiLCJfdHdvRGVjczMiLCJfdGhyZWVEZWNzIiwiaW5pdGlhbCIsInVuZGVmaW5lZCIsImlucHV0IiwiY29tcHV0ZSIsIkMiLCJ0aHJlZSIsImVsIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwiYm9keSIsImFwcGVuZCIsInNoYWRvd1Jvb3QiLCJ0ZXh0Q29udGVudCIsIlByb21pc2UiLCJyZXNvbHZlIiwic2V0QXR0cmlidXRlIiwiZWwxIiwiZWwyIiwiZGVmYXVsdFR3byIsIl9pbml0UHJvdG81IiwiX2JhZERlY3MiLCJfaW5pdF9iYWQiLCJCYWRUZXN0RWxlbWVudCIsImJhZCIsInBhc3NlZCIsIm1lc3NhZ2UiLCJjb25uZWN0ZWRDYWxsYmFjayIsImVycm9yIiwiZXhwZWN0ZWQiXSwic291cmNlcyI6WyJ0ZXN0LWRlZmF1bHQtcHJvcGVydGllcy5zcmMuanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYXNzZXJ0LCBpdCB9IGZyb20gJ0BuZXRmbGl4L3gtdGVzdC94LXRlc3QuanMnO1xuaW1wb3J0IHsgWEVsZW1lbnQsIHByb3BlcnR5LCBodG1sIH0gZnJvbSAnLi4veC1lbGVtZW50LW5leHQuanMnO1xuXG5jbGFzcyBUZXN0RWxlbWVudEJhc2ljIGV4dGVuZHMgWEVsZW1lbnQge1xuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGRlZmF1bHQ6ICdvbmUnIH0pXG4gIGFjY2Vzc29yIG9uZTtcblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm9uZX08L2Rpdj5gO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1iYXNpYy1uZXh0JywgVGVzdEVsZW1lbnRCYXNpYyk7XG5cbmNsYXNzIFRlc3RFbGVtZW50QW5vbnltb3VzIGV4dGVuZHMgWEVsZW1lbnQge1xuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGRlZmF1bHQ6ICgoKSA9PiAnb25lJykoKSB9KVxuICBhY2Nlc3NvciBvbmU7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogT2JqZWN0LCBkZWZhdWx0OiAoKSA9PiAoeyB0d286ICd0d28nIH0pIH0pXG4gIGFjY2Vzc29yIHR3bztcblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm9uZX0sICR7aG9zdC50d28udHdvfTwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWFub255bW91cy1uZXh0JywgVGVzdEVsZW1lbnRBbm9ueW1vdXMpO1xuXG5jbGFzcyBUZXN0RWxlbWVudFN0YXRpYyBleHRlbmRzIFhFbGVtZW50IHtcbiAgc3RhdGljIGdldCBvbmUoKSB7XG4gICAgcmV0dXJuICdvbmUnO1xuICB9XG4gIHN0YXRpYyBnZXRPbmUoKSB7XG4gICAgcmV0dXJuIHRoaXMub25lO1xuICB9XG4gIHN0YXRpYyBnZXQgdHdvKCkge1xuICAgIHJldHVybiB7IHR3bzogJ3R3bycgfTtcbiAgfVxuICBzdGF0aWMgZ2V0VHdvKCkge1xuICAgIHJldHVybiB0aGlzLnR3bztcbiAgfVxuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgZGVmYXVsdDogKCkgPT4gVGVzdEVsZW1lbnRTdGF0aWMuZ2V0T25lKCkgfSlcbiAgYWNjZXNzb3Igb25lO1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE9iamVjdCwgZGVmYXVsdDogKCkgPT4gVGVzdEVsZW1lbnRTdGF0aWMuZ2V0VHdvKCkgfSlcbiAgYWNjZXNzb3IgdHdvO1xuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3Qub25lfSwgJHtob3N0LnR3by50d299PC9kaXY+YDtcbiAgfVxufVxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtc3RhdGljLW5leHQnLCBUZXN0RWxlbWVudFN0YXRpYyk7XG5cbmNsYXNzIFRlc3RFbGVtZW50RWRnZSBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiB1bmRlZmluZWQsIGRlZmF1bHQ6ICdvbmUnIH0pXG4gIGFjY2Vzc29yIG9uZTtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBPYmplY3QsIGluaXRpYWw6IG51bGwsIGRlZmF1bHQ6ICgpID0+ICh7IHR3bzogJ3R3bycgfSkgfSlcbiAgYWNjZXNzb3IgdHdvO1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5wdXQ6IFtdLCBjb21wdXRlOiAoKSA9PiB7fSwgZGVmYXVsdDogJ3RocmVlJyB9KVxuICBhY2Nlc3NvciB0aHJlZTtcblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm9uZX0sICR7aG9zdC50d28udHdvfSwgJHtob3N0LnRocmVlfTwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWVkZ2UtbmV4dCcsIFRlc3RFbGVtZW50RWRnZSk7XG5cbml0KCdiYXNpYyBkZWZhdWx0IHByb3BlcnRpZXMnLCBhc3luYyAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZScpO1xuXG4gIC8vIFRlc3QgdGhhdCB3ZSBjYW4gb3ZlcnJpZGUgdGhlIGRlZmF1bHQuXG4gIGVsLm9uZSA9ICdPTkUnO1xuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdPTkUnKTtcblxuICAvLyBUZXN0IHRoYXQgd2UgZ2V0IG91ciBkZWZhdWx0IGJhY2sgdmlhIHVuZGVmaW5lZC5cbiAgZWwub25lID0gdW5kZWZpbmVkO1xuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUnKTtcblxuICAvLyBPdmVycmlkZSBhZ2Fpbi4uLlxuICBlbC5vbmUgPSAnT05FJztcbiAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnT05FJyk7XG5cbiAgLy8gVGVzdCB0aGF0IHdlIGdldCBvdXIgZGVmYXVsdCBiYWNrIHZpYSBudWxsLlxuICBlbC5vbmUgPSBudWxsO1xuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUnKTtcbn0pO1xuXG5pdCgnYmFzaWMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIHVuZGVmaW5lZCBwcm9wZXJ0aWVzKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYmFzaWMtbmV4dCcpO1xuICBlbC5vbmUgPSB1bmRlZmluZWQ7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUnKTtcbn0pO1xuXG5pdCgnYmFzaWMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIG51bGwgcHJvcGVydGllcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnKTtcbiAgZWwub25lID0gbnVsbDtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZScpO1xufSk7XG5cbml0KCdiYXNpYyBkZWZhdWx0IHByb3BlcnRpZXMgKHByZWRlZmluZWQgcHJvcGVydGllcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnKTtcbiAgZWwub25lID0gJ09ORSc7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdPTkUnKTtcbn0pO1xuXG5pdCgnYmFzaWMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIGF0dHJpYnV0ZXMpJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1iYXNpYy1uZXh0Jyk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnb25lJywgJ09ORScpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnT05FJyk7XG59KTtcblxuaXQoJ2Fub255bW91cyBkZWZhdWx0IHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWFub255bW91cy1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUsIHR3bycpO1xufSk7XG5cbml0KCdhbm9ueW1vdXMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIHVuZGVmaW5lZCBwcm9wZXJ0aWVzKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYW5vbnltb3VzLW5leHQnKTtcbiAgZWwub25lID0gdW5kZWZpbmVkO1xuICBlbC50d28gPSB1bmRlZmluZWQ7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUsIHR3bycpO1xufSk7XG5cbml0KCdhbm9ueW1vdXMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIG51bGwgcHJvcGVydGllcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWFub255bW91cy1uZXh0Jyk7XG4gIGVsLm9uZSA9IG51bGw7XG4gIGVsLnR3byA9IG51bGw7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUsIHR3bycpO1xufSk7XG5cbml0KCdhbm9ueW1vdXMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIHByb3BlcnRpZXMpJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1hbm9ueW1vdXMtbmV4dCcpO1xuICBlbC5vbmUgPSAnT05FJztcbiAgZWwudHdvID0geyB0d286ICdUV08nIH07XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdPTkUsIFRXTycpO1xufSk7XG5cbml0KCdhbm9ueW1vdXMgZGVmYXVsdCBwcm9wZXJ0aWVzIChwcmVkZWZpbmVkIGF0dHJpYnV0ZXMpJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1hbm9ueW1vdXMtbmV4dCcpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ29uZScsICdPTkUnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ09ORSwgdHdvJyk7XG59KTtcblxuaXQoJ2luaXRpYWwgKyBkZWZhdWx0ICYgY29tcHV0ZWQgKyBkZWZhdWx0IHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWVkZ2UtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnb25lLCB0d28sIHRocmVlJyk7XG59KTtcblxuaXQoJ2RlZmF1bHQgdmFsdWVzIGZyb20gZnVuY3Rpb25zIGFyZSB1bmlxdWUgcGVyIGluc3RhbmNlJywgKCkgPT4ge1xuICBjb25zdCBlbDEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtZWRnZS1uZXh0Jyk7XG4gIGNvbnN0IGVsMiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1lZGdlLW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwxLCBlbDIpO1xuICBhc3NlcnQoZWwxLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUsIHR3bywgdGhyZWUnKTtcbiAgYXNzZXJ0KGVsMi5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnb25lLCB0d28sIHRocmVlJyk7XG4gIGFzc2VydChlbDEudHdvICE9PSBlbDIudHdvKTtcbn0pO1xuXG5pdCgnZGVmYXVsdCB2YWx1ZXMgZnJvbSBmdW5jdGlvbnMgcGVyc2lzdCBwZXIgaW5zdGFuY2UnLCBhc3luYyAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWVkZ2UtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnb25lLCB0d28sIHRocmVlJyk7XG4gIGNvbnN0IGRlZmF1bHRUd28gPSBlbC50d287XG4gIGVsLnR3byA9IHsgdHdvOiAnVFdPJyB9O1xuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdvbmUsIFRXTywgdGhyZWUnKTtcbiAgYXNzZXJ0KGVsLnR3byAhPT0gZGVmYXVsdFR3byk7XG4gIGVsLnR3byA9IHVuZGVmaW5lZDtcbiAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnb25lLCB0d28sIHRocmVlJyk7XG4gIGFzc2VydChlbC50d28gPT09IGRlZmF1bHRUd28pO1xufSk7XG5cbml0KCdjYW5ub3Qgc2V0IGRlZmF1bHQgdG8gYSBiYWQgdHlwZScsICgpID0+IHtcbiAgY2xhc3MgQmFkVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBkZWZhdWx0OiAwIH0pXG4gICAgYWNjZXNzb3IgYmFkO1xuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTEnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHZhbHVlIGZvciBcIkJhZFRlc3RFbGVtZW50LnByb3RvdHlwZS5iYWRcIiAoZXhwZWN0ZWQgU3RyaW5nLCBnb3QgTnVtYmVyKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLFNBQVNBLE1BQU0sRUFBRUMsRUFBRSxRQUFRLDJCQUEyQjtBQUN0RCxTQUFTQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsSUFBSSxRQUFRLHNCQUFzQjtBQUUvRCxNQUFNQyxnQkFBZ0IsU0FBU0gsUUFBUSxDQUFDO0VBQUE7SUFBQSxDQUFBSSxTQUFBLEVBQUFDLFVBQUEsSUFBQUMsVUFBQSxTQUFBQyxRQUFBLDZCQUFUUCxRQUFRLEVBQUFRLENBQUE7RUFBQTtFQUFBLENBQUFDLENBQUEsSUFBQUosVUFBQSxRQUFBRCxTQUFBO0VBQUEsTUFBQUcsUUFBQSxHQUNwQ04sUUFBUSxDQUFDO0lBQUVTLElBQUksRUFBRUMsTUFBTTtJQUFFQyxPQUFPLEVBQUU7RUFBTSxDQUFDLENBQUM7SUFBQSxhQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUNsQ0ksR0FBR0EsQ0FBQUMsQ0FBQTtJQUFBLE1BQUFMLENBQUEsR0FBQUssQ0FBQTtFQUFBO0VBRVosT0FBT0MsUUFBUUEsQ0FBQ0MsSUFBSSxFQUFFO0lBQ3BCLE9BQU9kLElBQUksUUFBUWMsSUFBSSxDQUFDSCxHQUFHLFFBQVE7RUFDckM7QUFDRjtBQUNBSSxjQUFjLENBQUNDLE1BQU0s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RSxDQUFDSyxVQUFVLENBQUNDLFdBQVcsS0FBSyxpQkFBaUIsQ0FBQztFQUN2RDFELE1BQU0sQ0FBQ29ELEVBQUUsQ0FBQ3hCLEdBQUcsS0FBS29DLFVBQVUsQ0FBQztFQUM3QlosRUFBRSxDQUFDeEIsR0FBRyxHQUFHbUIsU0FBUztFQUNsQixNQUFNWSxPQUFPLENBQUNDLE9BQU8sQ0FBQyxDQUFDO0VBQ3ZCNUQsTUFBTSxDQUFDb0QsRUFBRSxDQUFDSyxVQUFVLENBQUNDLFdBQVcsS0FBSyxpQkFBaUIsQ0FBQztFQUN2RDFELE1BQU0sQ0FBQ29ELEVBQUUsQ0FBQ3hCLEdBQUcsS0FBS29DLFVBQVUsQ0FBQztBQUMvQixDQUFDLENBQUM7QUFFRi9ELEVBQUUsQ0FBQyxrQ0FBa0MsRUFBRSxNQUFNO0VBQUEsSUFBQWdFLFdBQUEsRUFBQUMsUUFBQSxFQUFBQyxTQUFBO0VBQzNDLE1BQU1DLGNBQWMsU0FBU2xFLFFBQVEsQ0FBQztJQUFBO01BQUEsQ0FBQWlFLFNBQUEsRUFBQUYsV0FBQSxJQUFBekQsVUFBQSxTQUFBMEQsUUFBQSw2QkFBVGhFLFFBQVEsRUFBQVEsQ0FBQTtJQUFBO0lBQUEsQ0FBQUMsQ0FBQSxJQUFBc0QsV0FBQSxRQUFBRSxTQUFBO0lBQUEsTUFBQUQsUUFBQSxHQUNsQy9ELFFBQVEsQ0FBQztNQUFFUyxJQUFJLEVBQUVDLE1BQU07TUFBRUMsT0FBTyxFQUFFO0lBQUUsQ0FBQyxDQUFDO01BQUEsYUFBQUgsQ0FBQTtJQUFBO0lBQUEsSUFDOUIwRCxHQUFHQSxDQUFBckQsQ0FBQTtNQUFBLE1BQUFMLENBQUEsR0FBQUssQ0FBQTtJQUFBO0VBQ2Q7RUFDQUcsY0FBYyxDQUFDQyxNQUFNLENBQUMseUJBQXlCLEVBQUVnRCxjQUFjLENBQUM7RUFDaEUsTUFBTWhCLEVBQUUsR0FBRyxJQUFJZ0IsY0FBYyxDQUFDLENBQUM7RUFDL0IsSUFBSUUsTUFBTSxHQUFHLEtBQUs7RUFDbEIsSUFBSUMsT0FBTyxHQUFHLHFCQUFxQjtFQUNuQyxJQUFJO0lBQ0ZuQixFQUFFLENBQUNvQixpQkFBaUIsQ0FBQyxDQUFDO0VBQ3hCLENBQUMsQ0FBQyxPQUFPQyxLQUFLLEVBQUU7SUFDZCxNQUFNQyxRQUFRLEdBQUcsb0ZBQW9GO0lBQ3JHSCxPQUFPLEdBQUdFLEtBQUssQ0FBQ0YsT0FBTztJQUN2QkQsTUFBTSxHQUFHRyxLQUFLLENBQUNGLE9BQU8sS0FBS0csUUFBUTtFQUNyQztFQUNBMUUsTUFBTSxDQUFDc0UsTUFBTSxFQUFFQyxPQUFPLENBQUM7QUFDekIsQ0FBQyxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/test-next/test-default-properties.src.js b/test-next/test-default-properties.src.js new file mode 100644 index 0000000..b502597 --- /dev/null +++ b/test-next/test-default-properties.src.js @@ -0,0 +1,207 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +class TestElementBasic extends XElement { + @property({ type: String, default: 'one' }) + accessor one; + + static template(host) { + return html`
${host.one}
`; + } +} +customElements.define('test-element-basic-next', TestElementBasic); + +class TestElementAnonymous extends XElement { + @property({ type: String, default: (() => 'one')() }) + accessor one; + + @property({ type: Object, default: () => ({ two: 'two' }) }) + accessor two; + + static template(host) { + return html`
${host.one}, ${host.two.two}
`; + } +} +customElements.define('test-element-anonymous-next', TestElementAnonymous); + +class TestElementStatic extends XElement { + static get one() { + return 'one'; + } + static getOne() { + return this.one; + } + static get two() { + return { two: 'two' }; + } + static getTwo() { + return this.two; + } + + @property({ type: String, default: () => TestElementStatic.getOne() }) + accessor one; + + @property({ type: Object, default: () => TestElementStatic.getTwo() }) + accessor two; + + static template(host) { + return html`
${host.one}, ${host.two.two}
`; + } +} +customElements.define('test-element-static-next', TestElementStatic); + +class TestElementEdge extends XElement { + @property({ type: String, initial: undefined, default: 'one' }) + accessor one; + + @property({ type: Object, initial: null, default: () => ({ two: 'two' }) }) + accessor two; + + @property({ type: String, input: [], compute: () => {}, default: 'three' }) + accessor three; + + static template(host) { + return html`
${host.one}, ${host.two.two}, ${host.three}
`; + } +} +customElements.define('test-element-edge-next', TestElementEdge); + +it('basic default properties', async () => { + const el = document.createElement('test-element-basic-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); + + // Test that we can override the default. + el.one = 'ONE'; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'ONE'); + + // Test that we get our default back via undefined. + el.one = undefined; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one'); + + // Override again... + el.one = 'ONE'; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'ONE'); + + // Test that we get our default back via null. + el.one = null; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic default properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic default properties (predefined null properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic default properties (predefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = 'ONE'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); + +it('basic default properties (predefined attributes)', () => { + const el = document.createElement('test-element-basic-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); + +it('anonymous default properties', () => { + const el = document.createElement('test-element-anonymous-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous default properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = undefined; + el.two = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous default properties (predefined null properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = null; + el.two = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous default properties (predefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = 'ONE'; + el.two = { two: 'TWO' }; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); + +it('anonymous default properties (predefined attributes)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, two'); +}); + +it('initial + default & computed + default properties', () => { + const el = document.createElement('test-element-edge-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two, three'); +}); + +it('default values from functions are unique per instance', () => { + const el1 = document.createElement('test-element-edge-next'); + const el2 = document.createElement('test-element-edge-next'); + document.body.append(el1, el2); + assert(el1.shadowRoot.textContent === 'one, two, three'); + assert(el2.shadowRoot.textContent === 'one, two, three'); + assert(el1.two !== el2.two); +}); + +it('default values from functions persist per instance', async () => { + const el = document.createElement('test-element-edge-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two, three'); + const defaultTwo = el.two; + el.two = { two: 'TWO' }; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one, TWO, three'); + assert(el.two !== defaultTwo); + el.two = undefined; + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'one, two, three'); + assert(el.two === defaultTwo); +}); + +it('cannot set default to a bad type', () => { + class BadTestElement extends XElement { + @property({ type: String, default: 0 }) + accessor bad; + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.bad" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-element-upgrade.html b/test-next/test-element-upgrade.html new file mode 100644 index 0000000..17bd322 --- /dev/null +++ b/test-next/test-element-upgrade.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Element Upgrade

+ + + diff --git a/test-next/test-element-upgrade.js b/test-next/test-element-upgrade.js new file mode 100644 index 0000000..68dc405 --- /dev/null +++ b/test-next/test-element-upgrade.js @@ -0,0 +1,114 @@ +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +export default class TestElement extends XElement { + constructor() { + super(); + this._readOnlyProperty = 'didelphidae'; + this._readOnlyKey = 'didelphimorphia'; + Reflect.defineProperty(this, 'readOnlyDefinedProperty', { + value: 'phalangeriformes', + configurable: false + }); + } + connectedCallback() { + super.connectedCallback(); + this.shadowRoot.firstElementChild.textContent = this.readOnlyProperty; + } + static template() { + return html`
`; + } + get readOnlyProperty() { + return this._readOnlyProperty; + } + get [Symbol.for('readOnlyKey')]() { + return this._readOnlyKey; + } + get reflectedProperty() { + return this.getAttribute('reflected-property'); + } + set reflectedProperty(value) { + this.setAttribute('reflected-property', value); + } +} +const setupEl = el => { + el.className = 'marsupialia'; + el.readOnlyProperty = 'chlamyphoridae'; + el[Symbol.for('readOnlyKey')] = 'dasypodidae'; + el.reflectedProperty = 'plantigrade'; +}; +const hasNotUpgraded = el => { + return el instanceof HTMLElement && el instanceof TestElement === false && el.shadowRoot === null && el.getAttribute('class') === 'marsupialia' && el.readOnlyProperty === 'chlamyphoridae' && el[Symbol.for('readOnlyKey')] === 'dasypodidae' && el.readOnlyDefinedProperty === undefined && el.reflectedProperty === 'plantigrade' && el.getAttribute('reflected-property') === null; +}; +const hasUpgraded = el => { + // Properties are still shadowed after upgrade and before initialization. + return el instanceof TestElement && el.getAttribute('class') === 'marsupialia' && el.readOnlyProperty === 'didelphidae' && el[Symbol.for('readOnlyKey')] === 'didelphimorphia' && el.readOnlyDefinedProperty === 'phalangeriformes' && el.reflectedProperty === 'plantigrade' && el.getAttribute('reflected-property') === 'plantigrade'; +}; +it('x-element upgrade lifecycle', () => { + const localName = 'test-element-next'; + assert(customElements.get(localName) === undefined, 'localName is initially undefined'); + const el1 = document.createElement(localName); + el1.id = 'el1'; + setupEl(el1); + document.body.append(el1); + const el2 = document.createElement(localName); + el2.id = 'el2'; + setupEl(el2); + assert(el1.localName === localName && document.getElementById('el1') === el1 && hasNotUpgraded(el1), 'el1 is setup as expected'); + assert(el1.localName === localName && document.getElementById('el2') === null && hasNotUpgraded(el2), 'el2 is setup as expected'); + customElements.define(localName, TestElement); + const el3 = document.createElement(localName); + el3.id = 'el3'; + el3.className = 'marsupialia'; + el3.reflectedProperty = 'plantigrade'; + const el4 = new TestElement(); + el4.id = 'el4'; + el4.className = 'marsupialia'; + el4.reflectedProperty = 'plantigrade'; + assert(hasUpgraded(el3) && hasUpgraded(el4), 'elements created after definition do not need upgrading'); + assert(hasUpgraded(el1), 'element in document is upgraded upon definition'); + assert(el1.shadowRoot.textContent === 'didelphidae', 'element in document synchronously renders'); + assert(hasNotUpgraded(el2), 'element out of document is still not upgraded'); + document.body.append(el2); + assert(el2.shadowRoot.textContent === 'didelphidae', 'element out of document upgrades/renders after being added'); + document.body.append(el3); + assert(el3.shadowRoot.textContent === 'didelphidae', 'element created after definition upgrades/renders after being added'); +}); +it('preserves x-element properties set before customElements.define', () => { + let _initProto, _fooDecs, _init_foo, _barDecs, _init_bar; + class PreUpgradeElement extends XElement { + static { + [_init_foo, _init_bar, _initProto] = _applyDecs(this, [[_fooDecs, 1, "foo"], [_barDecs, 1, "bar"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_foo(this)); + get [(_fooDecs = property({ + type: String + }), _barDecs = property({ + type: Number + }), "foo")]() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + #B = _init_bar(this); + get bar() { + return this.#B; + } + set bar(v) { + this.#B = v; + } + } + const el = document.createElement('pre-upgrade-element-next'); + el.foo = 'test'; + el.bar = 42; + customElements.define('pre-upgrade-element-next', PreUpgradeElement); + document.body.append(el); + assert(el.foo === 'test'); + assert(el.bar === 42); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnQiLCJjb25zdHJ1Y3RvciIsIl9yZWFkT25seVByb3BlcnR5IiwiX3JlYWRPbmx5S2V5IiwiUmVmbGVjdCIsImRlZmluZVByb3BlcnR5IiwidmFsdWUiLCJjb25maWd1cmFibGUiLCJjb25uZWN0ZWRDYWxsYmFjayIsInNoYWRvd1Jvb3QiLCJmaXJzdEVsZW1lbnRDaGlsZCIsInRleHRDb250ZW50IiwicmVhZE9ubHlQcm9wZXJ0eSIsInRlbXBsYXRlIiwiU3ltYm9sIiwiZm9yIiwicmVmbGVjdGVkUHJvcGVydHkiLCJnZXRBdHRyaWJ1dGUiLCJzZXRBdHRyaWJ1dGUiLCJzZXR1cEVsIiwiZWwiLCJjbGFzc05hbWUiLCJoYXNOb3RVcGdyYWRlZCIsIkhUTUxFbGVtZW50IiwicmVhZE9ubHlEZWZpbmVkUHJvcGVydHkiLCJ1bmRlZmluZWQiLCJoYXNVcGdyYWRlZCIsImxvY2FsTmFtZSIsImN1c3RvbUVsZW1lbnRzIiwiZ2V0IiwiZWwxIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwiaWQiLCJib2R5IiwiYXBwZW5kIiwiZWwyIiwiZ2V0RWxlbWVudEJ5SWQiLCJkZWZpbmUiLCJlbDMiLCJlbDQiLCJfaW5pdFByb3RvIiwiX2Zvb0RlY3MiLCJfaW5pdF9mb28iLCJfYmFyRGVjcyIsIl9pbml0X2JhciIsIlByZVVwZ3JhZGVFbGVtZW50IiwiX2FwcGx5RGVjcyIsImUiLCJBIiwidHlwZSIsIlN0cmluZyIsIk51bWJlciIsImZvbyIsInYiLCJCIiwiYmFyIl0sInNvdXJjZXMiOlsidGVzdC1lbGVtZW50LXVwZ3JhZGUuc3JjLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGFzc2VydCwgaXQgfSBmcm9tICdAbmV0ZmxpeC94LXRlc3QveC10ZXN0LmpzJztcbmltcG9ydCB7IFhFbGVtZW50LCBwcm9wZXJ0eSwgaHRtbCB9IGZyb20gJy4uL3gtZWxlbWVudC1uZXh0LmpzJztcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5fcmVhZE9ubHlQcm9wZXJ0eSA9ICdkaWRlbHBoaWRhZSc7XG4gICAgdGhpcy5fcmVhZE9ubHlLZXkgPSAnZGlkZWxwaGltb3JwaGlhJztcbiAgICBSZWZsZWN0LmRlZmluZVByb3BlcnR5KHRoaXMsICdyZWFkT25seURlZmluZWRQcm9wZXJ0eScsIHtcbiAgICAgIHZhbHVlOiAncGhhbGFuZ2VyaWZvcm1lcycsXG4gICAgICBjb25maWd1cmFibGU6IGZhbHNlLFxuICAgIH0pO1xuICB9XG5cbiAgY29ubmVjdGVkQ2FsbGJhY2soKSB7XG4gICAgc3VwZXIuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgICB0aGlzLnNoYWRvd1Jvb3QuZmlyc3RFbGVtZW50Q2hpbGQudGV4dENvbnRlbnQgPSB0aGlzLnJlYWRPbmx5UHJvcGVydHk7XG4gIH1cblxuICBzdGF0aWMgdGVtcGxhdGUoKSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj48L2Rpdj5gO1xuICB9XG5cbiAgZ2V0IHJlYWRPbmx5UHJvcGVydHkoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3JlYWRPbmx5UHJvcGVydHk7XG4gIH1cblxuICBnZXQgW1N5bWJvbC5mb3IoJ3JlYWRPbmx5S2V5JyldKCkge1xuICAgIHJldHVybiB0aGlzLl9yZWFkT25seUtleTtcbiAgfVxuXG4gIGdldCByZWZsZWN0ZWRQcm9wZXJ0eSgpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRBdHRyaWJ1dGUoJ3JlZmxlY3RlZC1wcm9wZXJ0eScpO1xuICB9XG5cbiAgc2V0IHJlZmxlY3RlZFByb3BlcnR5KHZhbHVlKSB7XG4gICAgdGhpcy5zZXRBdHRyaWJ1dGUoJ3JlZmxlY3RlZC1wcm9wZXJ0eScsIHZhbHVlKTtcbiAgfVxufVxuXG5jb25zdCBzZXR1cEVsID0gZWwgPT4ge1xuICBlbC5jbGFzc05hbWUgPSAnbWFyc3VwaWFsaWEnO1xuICBlbC5yZWFkT25seVByb3BlcnR5ID0gJ2NobGFteXBob3JpZGFlJztcbiAgZWxbU3ltYm9sLmZvcigncmVhZE9ubHlLZXknKV0gPSAnZGFzeXBvZGlkYWUnO1xuICBlbC5yZWZsZWN0ZWRQcm9wZXJ0eSA9ICdwbGFudGlncmFkZSc7XG59O1xuXG5jb25zdCBoYXNOb3RVcGdyYWRlZCA9IGVsID0+IHtcbiAgcmV0dXJuIChcbiAgICBlbCBpbnN0YW5jZW9mIEhUTUxFbGVtZW50ICYmXG4gICAgZWwgaW5zdGFuY2VvZiBUZXN0RWxlbWVudCA9PT0gZmFsc2UgJiZcbiAgICBlbC5zaGFkb3dSb290ID09PSBudWxsICYmXG4gICAgZWwuZ2V0QXR0cmlidXRlKCdjbGFzcycpID09PSAnbWFyc3VwaWFsaWEnICYmXG4gICAgZWwucmVhZE9ubHlQcm9wZXJ0eSA9PT0gJ2NobGFteXBob3JpZGFlJyAmJlxuICAgIGVsW1N5bWJvbC5mb3IoJ3JlYWRPbmx5S2V5JyldID09PSAnZGFzeXBvZGlkYWUnICYmXG4gICAgZWwucmVhZE9ubHlEZWZpbmVkUHJvcGVydHkgPT09IHVuZGVmaW5lZCAmJlxuICAgIGVsLnJlZmxlY3RlZFByb3BlcnR5ID09PSAncGxhbnRpZ3JhZGUnICYmXG4gICAgZWwuZ2V0QXR0cmlidXRlKCdyZWZsZWN0ZWQtcHJvcGVydHknKSA9PT0gbnVsbFxuICApO1xufTtcblxuY29uc3QgaGFzVXBncmFkZWQgPSBlbCA9PiB7XG4gIC8vIFByb3BlcnRpZXMgYXJlIHN0aWxsIHNoYWRvd2VkIGFmdGVyIHVwZ3JhZGUgYW5kIGJlZm9yZSBpbml0aWFsaXphdGlvbi5cbiAgcmV0dXJuIChcbiAgICBlbCBpbnN0YW5jZW9mIFRlc3RFbGVtZW50ICYmXG4gICAgZWwuZ2V0QXR0cmlidXRlKCdjbGFzcycpID09PSAnbWFyc3VwaWFsaWEnICYmXG4gICAgZWwucmVhZE9ubHlQcm9wZXJ0eSA9PT0gJ2RpZGVscGhpZGFlJyAmJlxuICAgIGVsW1N5bWJvbC5mb3IoJ3JlYWRPbmx5S2V5JyldID09PSAnZGlkZWxwaGltb3JwaGlhJyAmJlxuICAgIGVsLnJlYWRPbmx5RGVmaW5lZFByb3BlcnR5ID09PSAncGhhbGFuZ2VyaWZvcm1lcycgJiZcbiAgICBlbC5yZWZsZWN0ZWRQcm9wZXJ0eSA9PT0gJ3BsYW50aWdyYWRlJyAmJlxuICAgIGVsLmdldEF0dHJpYnV0ZSgncmVmbGVjdGVkLXByb3BlcnR5JykgPT09ICdwbGFudGlncmFkZSdcbiAgKTtcbn07XG5cbml0KCd4LWVsZW1lbnQgdXBncmFkZSBsaWZlY3ljbGUnLCAoKSA9PiB7XG4gIGNvbnN0IGxvY2FsTmFtZSA9ICd0ZXN0LWVsZW1lbnQtbmV4dCc7XG4gIGFzc2VydChcbiAgICBjdXN0b21FbGVtZW50cy5nZXQobG9jYWxOYW1lKSA9PT0gdW5kZWZpbmVkLFxuICAgICdsb2NhbE5hbWUgaXMgaW5pdGlhbGx5IHVuZGVmaW5lZCdcbiAgKTtcblxuICBjb25zdCBlbDEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KGxvY2FsTmFtZSk7XG4gIGVsMS5pZCA9ICdlbDEnO1xuICBzZXR1cEVsKGVsMSk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsMSk7XG5cbiAgY29uc3QgZWwyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChsb2NhbE5hbWUpO1xuICBlbDIuaWQgPSAnZWwyJztcbiAgc2V0dXBFbChlbDIpO1xuXG4gIGFzc2VydChcbiAgICBlbDEubG9jYWxOYW1lID09PSBsb2NhbE5hbWUgJiZcbiAgICAgIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdlbDEnKSA9PT0gZWwxICYmXG4gICAgICBoYXNOb3RVcGdyYWRlZChlbDEpLFxuICAgICdlbDEgaXMgc2V0dXAgYXMgZXhwZWN0ZWQnXG4gICk7XG5cbiAgYXNzZXJ0KFxuICAgIGVsMS5sb2NhbE5hbWUgPT09IGxvY2FsTmFtZSAmJlxuICAgICAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2VsMicpID09PSBudWxsICYmXG4gICAgICBoYXNOb3RVcGdyYWRlZChlbDIpLFxuICAgICdlbDIgaXMgc2V0dXAgYXMgZXhwZWN0ZWQnXG4gICk7XG5cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKGxvY2FsTmFtZSwgVGVzdEVsZW1lbnQpO1xuXG4gIGNvbnN0IGVsMyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQobG9jYWxOYW1lKTtcbiAgZWwzLmlkID0gJ2VsMyc7XG4gIGVsMy5jbGFzc05hbWUgPSAnbWFyc3VwaWFsaWEnO1xuICBlbDMucmVmbGVjdGVkUHJvcGVydHkgPSAncGxhbnRpZ3JhZGUnO1xuXG4gIGNvbnN0IGVsNCA9IG5ldyBUZXN0RWxlbWVudCgpO1xuICBlbDQuaWQgPSAnZWw0JztcbiAgZWw0LmNsYXNzTmFtZSA9ICdtYXJzdXBpYWxpYSc7XG4gIGVsNC5yZWZsZWN0ZWRQcm9wZXJ0eSA9ICdwbGFudGlncmFkZSc7XG5cbiAgYXNzZXJ0KFxuICAgIGhhc1VwZ3JhZGVkKGVsMykgJiYgaGFzVXBncmFkZWQoZWw0KSxcbiAgICAnZWxlbWVudHMgY3JlYXRlZCBhZnRlciBkZWZpbml0aW9uIGRvIG5vdCBuZWVkIHVwZ3JhZGluZydcbiAgKTtcblxuICBhc3NlcnQoaGFzVXBncmFkZWQoZWwxKSwgJ2VsZW1lbnQgaW4gZG9jdW1lbnQgaXMgdXBncmFkZWQgdXBvbiBkZWZpbml0aW9uJyk7XG4gIGFzc2VydChcbiAgICBlbDEuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ2RpZGVscGhpZGFlJyxcbiAgICAnZWxlbWVudCBpbiBkb2N1bWVudCBzeW5jaHJvbm91c2x5IHJlbmRlcnMnXG4gICk7XG5cbiAgYXNzZXJ0KGhhc05vdFVwZ3JhZGVkKGVsMiksICdlbGVtZW50IG91dCBvZiBkb2N1bWVudCBpcyBzdGlsbCBub3QgdXBncmFkZWQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwyKTtcbiAgYXNzZXJ0KFxuICAgIGVsMi5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnZGlkZWxwaGlkYWUnLFxuICAgICdlbGVtZW50IG91dCBvZiBkb2N1bWVudCB1cGdyYWRlcy9yZW5kZXJzIGFmdGVyIGJlaW5nIGFkZGVkJ1xuICApO1xuXG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsMyk7XG4gIGFzc2VydChcbiAgICBlbDMuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ2RpZGVscGhpZGFlJyxcbiAgICAnZWxlbWVudCBjcmVhdGVkIGFmdGVyIGRlZmluaXRpb24gdXBncmFkZXMvcmVuZGVycyBhZnRlciBiZWluZyBhZGRlZCdcbiAgKTtcbn0pO1xuXG5pdCgncHJlc2VydmVzIHgtZWxlbWVudCBwcm9wZXJ0aWVzIHNldCBiZWZvcmUgY3VzdG9tRWxlbWVudHMuZGVmaW5lJywgKCkgPT4ge1xuICBjbGFzcyBQcmVVcGdyYWRlRWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoe1xuICAgICAgdHlwZTogU3RyaW5nLFxuICAgIH0pXG4gICAgYWNjZXNzb3IgZm9vO1xuXG4gICAgQHByb3BlcnR5KHtcbiAgICAgIHR5cGU6IE51bWJlcixcbiAgICB9KVxuICAgIGFjY2Vzc29yIGJhcjtcbiAgfVxuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3ByZS11cGdyYWRlLWVsZW1lbnQtbmV4dCcpO1xuICBlbC5mb28gPSAndGVzdCc7XG4gIGVsLmJhciA9IDQyO1xuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3ByZS11cGdyYWRlLWVsZW1lbnQtbmV4dCcsIFByZVVwZ3JhZGVFbGVtZW50KTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuZm9vID09PSAndGVzdCcpO1xuICBhc3NlcnQoZWwuYmFyID09PSA0Mik7XG59KTtcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxTQUFTQSxNQUFNLEVBQUVDLEVBQUUsUUFBUSwyQkFBMkI7QUFDdEQsU0FBU0MsUUFBUSxFQUFFQyxRQUFRLEVBQUVDLElBQUksUUFBUSxzQkFBc0I7QUFFL0QsZUFBZSxNQUFNQyxXQUFXLFNBQVNILFFBQVEs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 \ No newline at end of file diff --git a/test-next/test-element-upgrade.src.js b/test-next/test-element-upgrade.src.js new file mode 100644 index 0000000..54d2e69 --- /dev/null +++ b/test-next/test-element-upgrade.src.js @@ -0,0 +1,161 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +export default class TestElement extends XElement { + constructor() { + super(); + this._readOnlyProperty = 'didelphidae'; + this._readOnlyKey = 'didelphimorphia'; + Reflect.defineProperty(this, 'readOnlyDefinedProperty', { + value: 'phalangeriformes', + configurable: false, + }); + } + + connectedCallback() { + super.connectedCallback(); + this.shadowRoot.firstElementChild.textContent = this.readOnlyProperty; + } + + static template() { + return html`
`; + } + + get readOnlyProperty() { + return this._readOnlyProperty; + } + + get [Symbol.for('readOnlyKey')]() { + return this._readOnlyKey; + } + + get reflectedProperty() { + return this.getAttribute('reflected-property'); + } + + set reflectedProperty(value) { + this.setAttribute('reflected-property', value); + } +} + +const setupEl = el => { + el.className = 'marsupialia'; + el.readOnlyProperty = 'chlamyphoridae'; + el[Symbol.for('readOnlyKey')] = 'dasypodidae'; + el.reflectedProperty = 'plantigrade'; +}; + +const hasNotUpgraded = el => { + return ( + el instanceof HTMLElement && + el instanceof TestElement === false && + el.shadowRoot === null && + el.getAttribute('class') === 'marsupialia' && + el.readOnlyProperty === 'chlamyphoridae' && + el[Symbol.for('readOnlyKey')] === 'dasypodidae' && + el.readOnlyDefinedProperty === undefined && + el.reflectedProperty === 'plantigrade' && + el.getAttribute('reflected-property') === null + ); +}; + +const hasUpgraded = el => { + // Properties are still shadowed after upgrade and before initialization. + return ( + el instanceof TestElement && + el.getAttribute('class') === 'marsupialia' && + el.readOnlyProperty === 'didelphidae' && + el[Symbol.for('readOnlyKey')] === 'didelphimorphia' && + el.readOnlyDefinedProperty === 'phalangeriformes' && + el.reflectedProperty === 'plantigrade' && + el.getAttribute('reflected-property') === 'plantigrade' + ); +}; + +it('x-element upgrade lifecycle', () => { + const localName = 'test-element-next'; + assert( + customElements.get(localName) === undefined, + 'localName is initially undefined' + ); + + const el1 = document.createElement(localName); + el1.id = 'el1'; + setupEl(el1); + document.body.append(el1); + + const el2 = document.createElement(localName); + el2.id = 'el2'; + setupEl(el2); + + assert( + el1.localName === localName && + document.getElementById('el1') === el1 && + hasNotUpgraded(el1), + 'el1 is setup as expected' + ); + + assert( + el1.localName === localName && + document.getElementById('el2') === null && + hasNotUpgraded(el2), + 'el2 is setup as expected' + ); + + customElements.define(localName, TestElement); + + const el3 = document.createElement(localName); + el3.id = 'el3'; + el3.className = 'marsupialia'; + el3.reflectedProperty = 'plantigrade'; + + const el4 = new TestElement(); + el4.id = 'el4'; + el4.className = 'marsupialia'; + el4.reflectedProperty = 'plantigrade'; + + assert( + hasUpgraded(el3) && hasUpgraded(el4), + 'elements created after definition do not need upgrading' + ); + + assert(hasUpgraded(el1), 'element in document is upgraded upon definition'); + assert( + el1.shadowRoot.textContent === 'didelphidae', + 'element in document synchronously renders' + ); + + assert(hasNotUpgraded(el2), 'element out of document is still not upgraded'); + document.body.append(el2); + assert( + el2.shadowRoot.textContent === 'didelphidae', + 'element out of document upgrades/renders after being added' + ); + + document.body.append(el3); + assert( + el3.shadowRoot.textContent === 'didelphidae', + 'element created after definition upgrades/renders after being added' + ); +}); + +it('preserves x-element properties set before customElements.define', () => { + class PreUpgradeElement extends XElement { + @property({ + type: String, + }) + accessor foo; + + @property({ + type: Number, + }) + accessor bar; + } + const el = document.createElement('pre-upgrade-element-next'); + el.foo = 'test'; + el.bar = 42; + customElements.define('pre-upgrade-element-next', PreUpgradeElement); + document.body.append(el); + assert(el.foo === 'test'); + assert(el.bar === 42); +}); diff --git a/test-next/test-inheritance.html b/test-next/test-inheritance.html new file mode 100644 index 0000000..2ac2f7f --- /dev/null +++ b/test-next/test-inheritance.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Inheritance

+ + + diff --git a/test-next/test-inheritance.js b/test-next/test-inheritance.js new file mode 100644 index 0000000..fcf4de2 --- /dev/null +++ b/test-next/test-inheritance.js @@ -0,0 +1,679 @@ +let _initProto, _parentPropDecs, _init_parentProp, _sharedPropDecs, _init_sharedProp, _initProto2, _childPropDecs, _init_childProp, _sharedPropDecs2, _init_sharedProp2, _initProto3, _initStatic, _parentClicksDecs, _init_parentClicks, _onParentEventDecs, _initProto4, _initStatic2, _childClicksDecs, _init_childClicks, _onChildEventDecs, _initProto5, _levelDecs, _init_level, _generationDecs, _init_generation, _initProto6, _levelDecs2, _init_level2, _generationDecs2, _init_generation2, _initProto7, _levelDecs3, _init_level3, _generationDecs3, _init_generation3; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener, html } from '../x-element-next.js'; + +// Test basic property inheritance +class ParentElement extends XElement { + static { + [_init_parentProp, _init_sharedProp, _initProto] = _applyDecs(this, [[_parentPropDecs, 1, "parentProp"], [_sharedPropDecs, 1, "sharedProp"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_parentProp(this)); + get [(_parentPropDecs = property({ + type: String, + initial: 'parent-value' + }), _sharedPropDecs = property({ + type: Number, + initial: 42 + }), "parentProp")]() { + return this.#A; + } + set parentProp(v) { + this.#A = v; + } + #B = _init_sharedProp(this); + get sharedProp() { + return this.#B; + } + set sharedProp(v) { + this.#B = v; + } + static template(host) { + return html`
${host.parentProp}-${host.sharedProp}
`; + } +} +class ChildElement extends ParentElement { + static { + [_init_childProp, _init_sharedProp2, _initProto2] = _applyDecs(this, [[_childPropDecs, 1, "childProp"], [_sharedPropDecs2, 1, "sharedProp"]], [], 0, void 0, ParentElement).e; + } + #A = (_initProto2(this), _init_childProp(this)); // Override parent property with different configuration + get [(_childPropDecs = property({ + type: String, + initial: 'child-value' + }), _sharedPropDecs2 = property({ + type: Number, + initial: 100 + }), "childProp")]() { + return this.#A; + } + set childProp(v) { + this.#A = v; + } + #B = _init_sharedProp2(this); + get sharedProp() { + return this.#B; + } + set sharedProp(v) { + this.#B = v; + } + static template(host) { + return html`
${host.parentProp}-${host.childProp}-${host.sharedProp}
`; + } +} + +// Test listener inheritance +class ParentWithListener extends XElement { + static { + [_init_parentClicks, _initProto3, _initStatic] = _applyDecs(this, [[_onParentEventDecs, 10, "onParentEvent"], [_parentClicksDecs, 1, "parentClicks"]], [], 0, void 0, XElement).e; + _initStatic(this); + } + #A = (_initProto3(this), _init_parentClicks(this)); + get [(_parentClicksDecs = property({ + type: Number, + initial: 0 + }), _onParentEventDecs = listener('parent-event'), "parentClicks")]() { + return this.#A; + } + set parentClicks(v) { + this.#A = v; + } + static onParentEvent(host) { + host.parentClicks++; + } + static template(host) { + return html`
parent: ${host.parentClicks}
`; + } +} +class ChildWithListener extends ParentWithListener { + static { + [_init_childClicks, _initProto4, _initStatic2] = _applyDecs(this, [[_onChildEventDecs, 10, "onChildEvent"], [_childClicksDecs, 1, "childClicks"]], [], 0, void 0, ParentWithListener).e; + _initStatic2(this); + } + #A = (_initProto4(this), _init_childClicks(this)); + get [(_childClicksDecs = property({ + type: Number, + initial: 0 + }), _onChildEventDecs = listener('child-event'), "childClicks")]() { + return this.#A; + } + set childClicks(v) { + this.#A = v; + } + static onChildEvent(host) { + host.childClicks++; + } + static template(host) { + return html`
parent: ${host.parentClicks}, child: ${host.childClicks}
`; + } +} + +// Test multi-level inheritance +class GrandparentLevel extends XElement { + static { + [_init_level, _init_generation, _initProto5] = _applyDecs(this, [[_levelDecs, 1, "level"], [_generationDecs, 1, "generation"]], [], 0, void 0, XElement).e; + } + #A = (_initProto5(this), _init_level(this)); + get [(_levelDecs = property({ + type: String, + initial: 'grandparent' + }), _generationDecs = property({ + type: Number, + initial: 1 + }), "level")]() { + return this.#A; + } + set level(v) { + this.#A = v; + } + #B = _init_generation(this); + get generation() { + return this.#B; + } + set generation(v) { + this.#B = v; + } +} +class ParentLevel extends GrandparentLevel { + static { + [_init_level2, _init_generation2, _initProto6] = _applyDecs(this, [[_levelDecs2, 1, "level"], [_generationDecs2, 1, "generation"]], [], 0, void 0, GrandparentLevel).e; + } + #A = (_initProto6(this), _init_level2(this)); + get [(_levelDecs2 = property({ + type: String, + initial: 'parent' + }), _generationDecs2 = property({ + type: Number, + initial: 2 + }), "level")]() { + return this.#A; + } + set level(v) { + this.#A = v; + } + #B = _init_generation2(this); + get generation() { + return this.#B; + } + set generation(v) { + this.#B = v; + } +} +class ChildLevel extends ParentLevel { + static { + [_init_level3, _init_generation3, _initProto7] = _applyDecs(this, [[_levelDecs3, 1, "level"], [_generationDecs3, 1, "generation"]], [], 0, void 0, ParentLevel).e; + } + #A = (_initProto7(this), _init_level3(this)); + get [(_levelDecs3 = property({ + type: String, + initial: 'child' + }), _generationDecs3 = property({ + type: Number, + initial: 3 + }), "level")]() { + return this.#A; + } + set level(v) { + this.#A = v; + } + #B = _init_generation3(this); + get generation() { + return this.#B; + } + set generation(v) { + this.#B = v; + } +} + +// Test private property inheritance +// class ParentWithPrivate extends XElement { +// @property({ type: String, initial: 'public-parent' }) +// accessor publicProp; + +// @property({ type: String, initial: 'private-parent' }) +// accessor #privateProp; + +// getPrivate() { +// return this.#privateProp; +// } + +// static template(host) { +// return html`
${host.publicProp}
`; +// } +// } + +// class ChildWithPrivate extends ParentWithPrivate { +// @property({ type: String, initial: 'public-child' }) +// accessor publicProp; + +// // Child can have its own private property with same name - no collision! +// @property({ type: String, initial: 'private-child' }) +// accessor #privateProp; + +// getChildPrivate() { +// return this.#privateProp; +// } + +// static template(host) { +// return html`
${host.publicProp}
`; +// } +// } + +customElements.define('test-parent-element', ParentElement); +customElements.define('test-child-element', ChildElement); +customElements.define('test-parent-with-listener', ParentWithListener); +customElements.define('test-child-with-listener', ChildWithListener); +customElements.define('test-grandparent-level', GrandparentLevel); +customElements.define('test-parent-level', ParentLevel); +customElements.define('test-child-level', ChildLevel); +// TODO: Babel doesn't support decorators on private accessors yet +// customElements.define('test-parent-with-private', ParentWithPrivate); +// customElements.define('test-child-with-private', ChildWithPrivate); + +it('child inherits parent properties', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + // Child should have both parent and child properties + assert(child.parentProp === 'parent-value', 'has parent property'); + assert(child.childProp === 'child-value', 'has child property'); + child.remove(); +}); +it('child can override parent properties', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + // Child overrides sharedProp with initial value of 100 instead of 42 + assert(child.sharedProp === 100, 'child overrides parent property'); + child.remove(); +}); +it('parent properties remain independent', () => { + const parent = document.createElement('test-parent-element'); + document.body.append(parent); + // Parent should not be affected by child's override + assert(parent.sharedProp === 42, 'parent property unchanged'); + assert(parent.childProp === undefined, 'parent does not have child property'); + parent.remove(); +}); +it('child inherits parent template behavior', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + const text = child.shadowRoot.textContent; + assert(text === 'parent-value-child-value-100', 'child renders with all properties'); + child.remove(); +}); +it('child inherits parent listeners', () => { + const child = document.createElement('test-child-with-listener'); + document.body.append(child); + + // Child should respond to both parent and child events + assert(child.parentClicks === 0, 'parent clicks starts at 0'); + assert(child.childClicks === 0, 'child clicks starts at 0'); + + // Events must be dispatched from within shadow root since listeners attach there + child.shadowRoot.dispatchEvent(new CustomEvent('parent-event', { + bubbles: true + })); + assert(child.parentClicks === 1, 'parent listener works in child'); + child.shadowRoot.dispatchEvent(new CustomEvent('child-event', { + bubbles: true + })); + assert(child.childClicks === 1, 'child listener works'); + child.remove(); +}); +it('parent listeners remain independent', () => { + const parent = document.createElement('test-parent-with-listener'); + document.body.append(parent); + + // Events must be dispatched from within shadow root since listeners attach there + parent.shadowRoot.dispatchEvent(new CustomEvent('parent-event', { + bubbles: true + })); + assert(parent.parentClicks === 1, 'parent listener works'); + + // Parent should not respond to child-only events + parent.shadowRoot.dispatchEvent(new CustomEvent('child-event', { + bubbles: true + })); + assert(parent.childClicks === undefined, 'parent does not have child listener'); + parent.remove(); +}); +it('multi-level inheritance works (grandparent → parent → child)', () => { + const grandparent = document.createElement('test-grandparent-level'); + const parent = document.createElement('test-parent-level'); + const child = document.createElement('test-child-level'); + document.body.append(grandparent, parent, child); + assert(grandparent.level === 'grandparent', 'grandparent has correct level'); + assert(grandparent.generation === 1, 'grandparent has correct generation'); + assert(parent.level === 'parent', 'parent overrides level'); + assert(parent.generation === 2, 'parent overrides generation'); + assert(child.level === 'child', 'child overrides level'); + assert(child.generation === 3, 'child overrides generation'); + grandparent.remove(); + parent.remove(); + child.remove(); +}); +it.skip('private properties do not collide across inheritance', () => { + const parent = document.createElement('test-parent-with-private'); + const child = document.createElement('test-child-with-private'); + document.body.append(parent, child); + + // Each class has its own private #privateProp with different values + assert(parent.getPrivate() === 'private-parent', 'parent has its private value'); + assert(child.getPrivate() === 'private-parent', 'child inherits parent getter'); + assert(child.getChildPrivate() === 'private-child', 'child has its own private value'); + + // Public properties override correctly + assert(parent.publicProp === 'public-parent', 'parent public prop'); + assert(child.publicProp === 'public-child', 'child overrides public prop'); + parent.remove(); + child.remove(); +}); +it('property modifications do not affect parent class', () => { + const parent = document.createElement('test-parent-element'); + const child = document.createElement('test-child-element'); + document.body.append(parent, child); + + // Modify child instance + child.parentProp = 'modified'; + child.sharedProp = 999; + + // Parent instance should be unaffected + assert(parent.parentProp === 'parent-value', 'parent instance unchanged'); + assert(parent.sharedProp === 42, 'parent instance unchanged'); + + // Create new parent to verify class definition unchanged + const parent2 = document.createElement('test-parent-element'); + document.body.append(parent2); + assert(parent2.parentProp === 'parent-value', 'parent class definition unchanged'); + assert(parent2.sharedProp === 42, 'parent class definition unchanged'); + parent.remove(); + child.remove(); + parent2.remove(); +}); +it('out-of-order analysis (child analyzed before parent)', () => { + let _initProto8, _parentValueDecs, _init_parentValue, _sharedValueDecs, _init_sharedValue, _initProto9, _childValueDecs, _init_childValue, _sharedValueDecs2, _init_sharedValue2; + // Define new parent and child classes inline to ensure fresh analysis + class OutOfOrderParent extends XElement { + static { + [_init_parentValue, _init_sharedValue, _initProto8] = _applyDecs(this, [[_parentValueDecs, 1, "parentValue"], [_sharedValueDecs, 1, "sharedValue"]], [], 0, void 0, XElement).e; + } + #A = (_initProto8(this), _init_parentValue(this)); + get [(_parentValueDecs = property({ + type: String, + initial: 'parent-ooo' + }), _sharedValueDecs = property({ + type: Number, + initial: 10 + }), "parentValue")]() { + return this.#A; + } + set parentValue(v) { + this.#A = v; + } + #B = _init_sharedValue(this); + get sharedValue() { + return this.#B; + } + set sharedValue(v) { + this.#B = v; + } + } + class OutOfOrderChild extends OutOfOrderParent { + static { + [_init_childValue, _init_sharedValue2, _initProto9] = _applyDecs(this, [[_childValueDecs, 1, "childValue"], [_sharedValueDecs2, 1, "sharedValue"]], [], 0, void 0, OutOfOrderParent).e; + } + #A = (_initProto9(this), _init_childValue(this)); + get [(_childValueDecs = property({ + type: String, + initial: 'child-ooo' + }), _sharedValueDecs2 = property({ + type: Number, + initial: 20 + }), "childValue")]() { + return this.#A; + } + set childValue(v) { + this.#A = v; + } + #B = _init_sharedValue2(this); + get sharedValue() { + return this.#B; + } + set sharedValue(v) { + this.#B = v; + } + } + customElements.define('test-ooo-parent', OutOfOrderParent); + customElements.define('test-ooo-child', OutOfOrderChild); + + // Create child FIRST - this triggers analysis of child, which should + // automatically trigger parent analysis first to ensure proper inheritance + const child = document.createElement('test-ooo-child'); + document.body.append(child); + + // Verify child has correct values (would fail if parent wasn't analyzed first) + assert(child.parentValue === 'parent-ooo', 'child inherits parent property'); + assert(child.childValue === 'child-ooo', 'child has its own property'); + assert(child.sharedValue === 20, 'child overrides parent property'); + + // Now create parent and verify it's unaffected by child + const parent = document.createElement('test-ooo-parent'); + document.body.append(parent); + assert(parent.parentValue === 'parent-ooo', 'parent property correct'); + assert(parent.sharedValue === 10, 'parent property not affected by child'); + assert(parent.childValue === undefined, 'parent does not have child property'); + child.remove(); + parent.remove(); +}); +it('parent and child both listen to same event type', () => { + let _initProto0, _initStatic3, _parentCountDecs, _init_parentCount, _onParentSharedDecs, _initProto1, _initStatic4, _childCountDecs, _init_childCount, _onChildSharedDecs; + // Parent and child each have different methods listening to same event type + class ParentMultiListener extends XElement { + static { + [_init_parentCount, _initProto0, _initStatic3] = _applyDecs(this, [[_onParentSharedDecs, 10, "onParentShared"], [_parentCountDecs, 1, "parentCount"]], [], 0, void 0, XElement).e; + _initStatic3(this); + } + #A = (_initProto0(this), _init_parentCount(this)); + get [(_parentCountDecs = property({ + type: Number, + initial: 0 + }), _onParentSharedDecs = listener('shared-event'), "parentCount")]() { + return this.#A; + } + set parentCount(v) { + this.#A = v; + } + static onParentShared(host) { + host.parentCount++; + } + static template(host) { + return html`
parent: ${host.parentCount}
`; + } + } + class ChildMultiListener extends ParentMultiListener { + static { + [_init_childCount, _initProto1, _initStatic4] = _applyDecs(this, [[_onChildSharedDecs, 10, "onChildShared"], [_childCountDecs, 1, "childCount"]], [], 0, void 0, ParentMultiListener).e; + _initStatic4(this); + } + #A = (_initProto1(this), _init_childCount(this)); + get [(_childCountDecs = property({ + type: Number, + initial: 0 + }), _onChildSharedDecs = listener('shared-event'), "childCount")]() { + return this.#A; + } + set childCount(v) { + this.#A = v; + } + static onChildShared(host) { + host.childCount++; + } + static template(host) { + return html`
parent: ${host.parentCount}, child: ${host.childCount}
`; + } + } + customElements.define('test-parent-multi', ParentMultiListener); + customElements.define('test-child-multi', ChildMultiListener); + const child = document.createElement('test-child-multi'); + document.body.append(child); + assert(child.parentCount === 0, 'parent count starts at 0'); + assert(child.childCount === 0, 'child count starts at 0'); + + // Dispatch shared event - both parent and child listeners should fire + child.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { + bubbles: true + })); + assert(child.parentCount === 1, 'parent listener fired'); + assert(child.childCount === 1, 'child listener fired'); + + // Fire again - both should increment + child.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { + bubbles: true + })); + assert(child.parentCount === 2, 'parent listener fired again'); + assert(child.childCount === 2, 'child listener fired again'); + + // Verify parent instance only has parent listener + const parent = document.createElement('test-parent-multi'); + document.body.append(parent); + parent.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { + bubbles: true + })); + assert(parent.parentCount === 1, 'parent listener works on parent instance'); + assert(parent.childCount === undefined, 'parent instance has no child listener'); + child.remove(); + parent.remove(); +}); +it('child overrides parent listener with same method name', () => { + let _initProto10, _initStatic5, _parentClicksDecs2, _init_parentClicks2, _onClickDecs, _initProto11, _initStatic6, _childClicksDecs2, _init_childClicks2, _onClickDecs2; + // When child redefines a method with the same name, only child's listener fires + class ParentWithMethod extends XElement { + static { + [_init_parentClicks2, _initProto10, _initStatic5] = _applyDecs(this, [[_onClickDecs, 10, "onClick"], [_parentClicksDecs2, 1, "parentClicks"]], [], 0, void 0, XElement).e; + _initStatic5(this); + } + #A = (_initProto10(this), _init_parentClicks2(this)); + get [(_parentClicksDecs2 = property({ + type: Number, + initial: 0 + }), _onClickDecs = listener('click'), "parentClicks")]() { + return this.#A; + } + set parentClicks(v) { + this.#A = v; + } + static onClick(host) { + host.parentClicks++; + } + static template(host) { + return html`
parent: ${host.parentClicks}
`; + } + } + class ChildOverridesMethod extends ParentWithMethod { + static { + [_init_childClicks2, _initProto11, _initStatic6] = _applyDecs(this, [[_onClickDecs2, 10, "onClick"], [_childClicksDecs2, 1, "childClicks"]], [], 0, void 0, ParentWithMethod).e; + _initStatic6(this); + } + #A = (_initProto11(this), _init_childClicks2(this)); + get [(_childClicksDecs2 = property({ + type: Number, + initial: 0 + }), _onClickDecs2 = listener('click'), "childClicks")]() { + return this.#A; + } + set childClicks(v) { + this.#A = v; + } + static onClick(host) { + // Same method name as parent - should override + host.childClicks++; + } + static template(host) { + return html`
parent: ${host.parentClicks}, child: ${host.childClicks}
`; + } + } + customElements.define('test-parent-with-method', ParentWithMethod); + customElements.define('test-child-overrides-method', ChildOverridesMethod); + const child = document.createElement('test-child-overrides-method'); + document.body.append(child); + assert(child.parentClicks === 0, 'parent clicks starts at 0'); + assert(child.childClicks === 0, 'child clicks starts at 0'); + + // Dispatch click event - only child's onClick should fire + child.shadowRoot.dispatchEvent(new CustomEvent('click', { + bubbles: true + })); + assert(child.parentClicks === 0, 'parent listener did not fire (overridden by child)'); + assert(child.childClicks === 1, 'child listener fired'); + + // Verify parent instance uses parent's onClick + const parent = document.createElement('test-parent-with-method'); + document.body.append(parent); + parent.shadowRoot.dispatchEvent(new CustomEvent('click', { + bubbles: true + })); + assert(parent.parentClicks === 1, 'parent listener works on parent instance'); + assert(parent.childClicks === undefined, 'parent instance has no child property'); + child.remove(); + parent.remove(); +}); +it('plain class without decorators (missing metadata link) works correctly', () => { + let _initProto12, _basePropDecs, _init_baseProp, _generationDecs4, _init_generation4, _initProto13, _childPropDecs2, _init_childProp2, _generationDecs5, _init_generation5; + // Test that a class without decorators in the middle of the inheritance chain + // works correctly and doesn't have Symbol.metadata polluted onto it + + class DecoratedBase extends XElement { + static { + [_init_baseProp, _init_generation4, _initProto12] = _applyDecs(this, [[_basePropDecs, 1, "baseProp"], [_generationDecs4, 1, "generation"]], [], 0, void 0, XElement).e; + } + #A = (_initProto12(this), _init_baseProp(this)); + get [(_basePropDecs = property({ + type: String, + initial: 'base-value' + }), _generationDecs4 = property({ + type: Number, + initial: 1 + }), "baseProp")]() { + return this.#A; + } + set baseProp(v) { + this.#A = v; + } + #B = _init_generation4(this); + get generation() { + return this.#B; + } + set generation(v) { + this.#B = v; + } + } + + // Plain class with NO decorators - this is the "missing link" + class PlainMiddle extends DecoratedBase { + // No decorators! Just plain class extending decorated base + } + class DecoratedChild extends PlainMiddle { + static { + [_init_childProp2, _init_generation5, _initProto13] = _applyDecs(this, [[_childPropDecs2, 1, "childProp"], [_generationDecs5, 1, "generation"]], [], 0, void 0, PlainMiddle).e; + } + #A = (_initProto13(this), _init_childProp2(this)); + get [(_childPropDecs2 = property({ + type: String, + initial: 'child-value' + }), _generationDecs5 = property({ + type: Number, + initial: 3 + }), "childProp")]() { + return this.#A; + } + set childProp(v) { + this.#A = v; + } + #B = _init_generation5(this); + get generation() { + return this.#B; + } + set generation(v) { + this.#B = v; + } + } + customElements.define('test-decorated-base-link', DecoratedBase); + customElements.define('test-plain-middle-link', PlainMiddle); + customElements.define('test-decorated-child-link', DecoratedChild); + + // CRITICAL: Verify PlainMiddle doesn't have Symbol.metadata as OWN property + // (it might inherit from parent via prototype chain, but shouldn't have its own) + assert(!Object.hasOwn(PlainMiddle, Symbol.metadata), 'plain class has no own Symbol.metadata property'); + + // Verify DecoratedBase and DecoratedChild DO have Symbol.metadata (from decorators) + assert(DecoratedBase[Symbol.metadata] !== undefined, 'decorated base has Symbol.metadata'); + assert(DecoratedChild[Symbol.metadata] !== undefined, 'decorated child has Symbol.metadata'); + + // Create instances and verify inheritance still works correctly + const base = document.createElement('test-decorated-base-link'); + document.body.append(base); + assert(base.baseProp === 'base-value', 'base has baseProp'); + assert(base.generation === 1, 'base has generation 1'); + assert(base.childProp === undefined, 'base has no child property'); + const middle = document.createElement('test-plain-middle-link'); + document.body.append(middle); + assert(middle.baseProp === 'base-value', 'middle inherits baseProp from base'); + assert(middle.generation === 1, 'middle inherits generation from base'); + assert(middle.childProp === undefined, 'middle has no child property'); + const child = document.createElement('test-decorated-child-link'); + document.body.append(child); + assert(child.baseProp === 'base-value', 'child inherits baseProp through plain middle'); + assert(child.childProp === 'child-value', 'child has childProp'); + assert(child.generation === 3, 'child overrides generation'); + + // Verify that modifications to child don't affect middle or base + child.baseProp = 'modified'; + assert(middle.baseProp === 'base-value', 'middle unchanged after child modification'); + assert(base.baseProp === 'base-value', 'base unchanged after child modification'); + base.remove(); + middle.remove(); + child.remove(); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJsaXN0ZW5lciIsImh0bWwiLCJQYXJlbnRFbGVtZW50IiwiX2luaXRfcGFyZW50UHJvcCIsIl9pbml0X3NoYXJlZFByb3AiLCJfaW5pdFByb3RvIiwiX2FwcGx5RGVjcyIsIl9wYXJlbnRQcm9wRGVjcyIsIl9zaGFyZWRQcm9wRGVjcyIsImUiLCJBIiwidHlwZSIsIlN0cmluZyIsImluaXRpYWwiLCJOdW1iZXIiLCJwYXJlbnRQcm9wIiwidiIsIkIiLCJzaGFyZWRQcm9wIiwidGVtcGxhdGUiLCJob3N0IiwiQ2hpbGRFbGVtZW50IiwiX2luaXRfY2hpbGRQcm9wIiwiX2luaXRfc2hhcmVkUHJvcDIiLCJfaW5pdFByb3RvMiIsIl9jaGlsZFByb3BEZWNzIiwiX3NoYXJlZFByb3BEZWNzMiIsImNoaWxkUHJvcCIsIlBhcmVudFdpdGhMaXN0ZW5lciIsIl9pbml0X3BhcmVudENsaWNrcyIsIl9pbml0UHJvdG8zIiwiX2luaXRTdGF0aWMiLCJfb25QYXJlbnRFdmVudERlY3MiLCJfcGFyZW50Q2xpY2tzRGVjcyIsInBhcmVudENsaWNrcyIsIm9uUGFyZW50RXZlbnQiLCJDaGlsZFdpdGhMaXN0ZW5lciIsIl9pbml0X2NoaWxkQ2xpY2tzIiwiX2luaXRQcm90bzQiLCJfaW5pdFN0YXRpYzIiLCJfb25DaGlsZEV2ZW50RGVjcyIsIl9jaGlsZENsaWNrc0RlY3MiLCJjaGlsZENsaWNrcyIsIm9uQ2hpbGRFdmVudCIsIkdyYW5kcGFyZW50TGV2ZWwiLCJfaW5pdF9sZXZlbCIsIl9pbml0X2dlbmVyYXRpb24iLCJfaW5pdFByb3RvNSIsIl9sZXZlbERlY3MiLCJfZ2VuZXJhdGlvbkRlY3MiLCJsZXZlbCIsImdlbmVyYXRpb24iLCJQYXJlbnRMZXZlbCIsIl9pbml0X2xldmVsMiIsIl9pbml0X2dlbmVyYXRpb24yIiwiX2luaXRQcm90bzYiLCJfbGV2ZWxEZWNzMiIsIl9nZW5lcmF0aW9uRGVjczIiLCJDaGlsZExldmVsIiwiX2luaXRfbGV2ZWwzIiwiX2luaXRfZ2VuZXJhdGlvbjMiLCJfaW5pdFByb3RvNyIsIl9sZXZlbERlY3MzIiwiX2dlbmVyYXRpb25EZWNzMyIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiY2hpbGQiLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJib2R5IiwiYXBwZW5kIiwicmVtb3ZlIiwicGFyZW50IiwidW5kZWZpbmVkIiwidGV4dCIsInNoYWRvd1Jvb3QiLCJ0ZXh0Q29udGVudCIsImRpc3BhdGNoRXZlbnQiLCJDdXN0b21FdmVudCIsImJ1YmJsZXMiLCJncmFuZHBhcmVudCIsInNraXAiLCJnZXRQcml2YXRlIiwiZ2V0Q2hpbGRQcml2YXRlIiwicHVibGljUHJvcCIsInBhcmVudDIiLCJfaW5pdFByb3RvOCIsIl9wYXJlbnRWYWx1ZURlY3MiLCJfaW5pdF9wYXJlbnRWYWx1ZSIsIl9zaGFyZWRWYWx1ZURlY3MiLCJfaW5pdF9zaGFyZWRWYWx1ZSIsIl9pbml0UHJvdG85IiwiX2NoaWxkVmFsdWVEZWNzIiwiX2luaXRfY2hpbGRWYWx1ZSIsIl9zaGFyZWRWYWx1ZURlY3MyIiwiX2luaXRfc2hhcmVkVmFsdWUyIiwiT3V0T2ZPcmRlclBhcmVudCIsInBhcmVudFZhbHVlIiwic2hhcmVkVmFsdWUiLCJPdXRPZk9yZGVyQ2hpbGQiLCJjaGlsZFZhbHVlIiwiX2luaXRQcm90bzAiLCJfaW5pdFN0YXRpYzMiLCJfcGFyZW50Q291bnREZWNzIiwiX2luaXRfcGFyZW50Q291bnQiLCJfb25QYXJlbnRTaGFyZWREZWNzIiwiX2luaXRQcm90bzEiLCJfaW5pdFN0YXRpYzQiLCJfY2hpbGRDb3VudERlY3MiLCJfaW5pdF9jaGlsZENvdW50IiwiX29uQ2hpbGRTaGFyZWREZWNzIiwiUGFyZW50TXVsdGlMaXN0ZW5lciIsInBhcmVudENvdW50Iiwib25QYXJlbnRTaGFyZWQiLCJDaGlsZE11bHRpTGlzdGVuZXIiLCJjaGlsZENvdW50Iiwib25DaGlsZFNoYXJlZCIsIl9pbml0UHJvdG8xMCIsIl9pbml0U3RhdGljNSIsIl9wYXJlbnRDbGlja3NEZWNzMiIsIl9pbml0X3BhcmVudENsaWNrczIiLCJfb25DbGlja0RlY3MiLCJfaW5pdFByb3RvMTEiLCJfaW5pdFN0YXRpYzYiLCJfY2hpbGRDbGlja3NEZWNzMiIsIl9pbml0X2NoaWxkQ2xpY2tzMiIsIl9vbkNsaWNrRGVjczIiLCJQYXJlbnRXaXRoTWV0aG9kIiwib25DbGljayIsIkNoaWxkT3ZlcnJpZGVzTWV0aG9kIiwiX2luaXRQcm90bzEyIiwiX2Jhc2VQcm9wRGVjcyIsIl9pbml0X2Jhc2VQcm9wIiwiX2dlbmVyYXRpb25EZWNzNCIsIl9pbml0X2dlbmVyYXRpb240IiwiX2luaXRQcm90bzEzIiwiX2NoaWxkUHJvcERlY3MyIiwiX2luaXRfY2hpbGRQcm9wMiIsIl9nZW5lcmF0aW9uRGVjczUiLCJfaW5pdF9nZW5lcmF0aW9uNSIsIkRlY29yYXRlZEJhc2UiLCJiYXNlUHJvcCIsIlBsYWluTWlkZGxlIiwiRGVjb3JhdGVkQ2hpbGQiLCJPYmplY3QiLCJoYXNPd24iLCJTeW1ib2wiLCJtZXRhZGF0YSIsImJhc2UiLCJtaWRkbGUiXSwic291cmNlcyI6WyJ0ZXN0LWluaGVyaXRhbmNlLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHksIGxpc3RlbmVyLCBodG1sIH0gZnJvbSAnLi4veC1lbGVtZW50LW5leHQuanMnO1xuXG4vLyBUZXN0IGJhc2ljIHByb3BlcnR5IGluaGVyaXRhbmNlXG5jbGFzcyBQYXJlbnRFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdwYXJlbnQtdmFsdWUnIH0pXG4gIGFjY2Vzc29yIHBhcmVudFByb3A7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiA0MiB9KVxuICBhY2Nlc3NvciBzaGFyZWRQcm9wO1xuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3QucGFyZW50UHJvcH0tJHtob3N0LnNoYXJlZFByb3B9PC9kaXY+YDtcbiAgfVxufVxuXG5jbGFzcyBDaGlsZEVsZW1lbnQgZXh0ZW5kcyBQYXJlbnRFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAnY2hpbGQtdmFsdWUnIH0pXG4gIGFjY2Vzc29yIGNoaWxkUHJvcDtcblxuICAvLyBPdmVycmlkZSBwYXJlbnQgcHJvcGVydHkgd2l0aCBkaWZmZXJlbnQgY29uZmlndXJhdGlvblxuICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDEwMCB9KVxuICBhY2Nlc3NvciBzaGFyZWRQcm9wO1xuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3QucGFyZW50UHJvcH0tJHtob3N0LmNoaWxkUHJvcH0tJHtob3N0LnNoYXJlZFByb3B9PC9kaXY+YDtcbiAgfVxufVxuXG4vLyBUZXN0IGxpc3RlbmVyIGluaGVyaXRhbmNlXG5jbGFzcyBQYXJlbnRXaXRoTGlzdGVuZXIgZXh0ZW5kcyBYRWxlbWVudCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciwgaW5pdGlhbDogMCB9KVxuICBhY2Nlc3NvciBwYXJlbnRDbGlja3M7XG5cbiAgQGxpc3RlbmVyKCdwYXJlbnQtZXZlbnQnKVxuICBzdGF0aWMgb25QYXJlbnRFdmVudChob3N0KSB7XG4gICAgaG9zdC5wYXJlbnRDbGlja3MrKztcbiAgfVxuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj5wYXJlbnQ6ICR7aG9zdC5wYXJlbnRDbGlja3N9PC9kaXY+YDtcbiAgfVxufVxuXG5jbGFzcyBDaGlsZFdpdGhMaXN0ZW5lciBleHRlbmRzIFBhcmVudFdpdGhMaXN0ZW5lciB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciwgaW5pdGlhbDogMCB9KVxuICBhY2Nlc3NvciBjaGlsZENsaWNrcztcblxuICBAbGlzdGVuZXIoJ2NoaWxkLWV2ZW50JylcbiAgc3RhdGljIG9uQ2hpbGRFdmVudChob3N0KSB7XG4gICAgaG9zdC5jaGlsZENsaWNrcysrO1xuICB9XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGA8ZGl2PnBhcmVudDogJHtob3N0LnBhcmVudENsaWNrc30sIGNoaWxkOiAke2hvc3QuY2hpbGRDbGlja3N9PC9kaXY+YDtcbiAgfVxufVxuXG4vLyBUZXN0IG11bHRpLWxldmVsIGluaGVyaXRhbmNlXG5jbGFzcyBHcmFuZHBhcmVudExldmVsIGV4dGVuZHMgWEVsZW1lbnQge1xuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdncmFuZHBhcmVudCcgfSlcbiAgYWNjZXNzb3IgbGV2ZWw7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAxIH0pXG4gIGFjY2Vzc29yIGdlbmVyYXRpb247XG59XG5cbmNsYXNzIFBhcmVudExldmVsIGV4dGVuZHMgR3JhbmRwYXJlbnRMZXZlbCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogJ3BhcmVudCcgfSlcbiAgYWNjZXNzb3IgbGV2ZWw7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAyIH0pXG4gIGFjY2Vzc29yIGdlbmVyYXRpb247XG59XG5cbmNsYXNzIENoaWxkTGV2ZWwgZXh0ZW5kcyBQYXJlbnRMZXZlbCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogJ2NoaWxkJyB9KVxuICBhY2Nlc3NvciBsZXZlbDtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDMgfSlcbiAgYWNjZXNzb3IgZ2VuZXJhdGlvbjtcbn1cblxuLy8gVGVzdCBwcml2YXRlIHByb3BlcnR5IGluaGVyaXRhbmNlXG4vLyBjbGFzcyBQYXJlbnRXaXRoUHJpdmF0ZSBleHRlbmRzIFhFbGVtZW50IHtcbi8vICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAncHVibGljLXBhcmVudCcgfSlcbi8vICAgYWNjZXNzb3IgcHVibGljUHJvcDtcblxuLy8gICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdwcml2YXRlLXBhcmVudCcgfSlcbi8vICAgYWNjZXNzb3IgI3ByaXZhdGVQcm9wO1xuXG4vLyAgIGdldFByaXZhdGUoKSB7XG4vLyAgICAgcmV0dXJuIHRoaXMuI3ByaXZhdGVQcm9wO1xuLy8gICB9XG5cbi8vICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbi8vICAgICByZXR1cm4gaHRtbGA8ZGl2PiR7aG9zdC5wdWJsaWNQcm9wfTwvZGl2PmA7XG4vLyAgIH1cbi8vIH1cblxuLy8gY2xhc3MgQ2hpbGRXaXRoUHJpdmF0ZSBleHRlbmRzIFBhcmVudFdpdGhQcml2YXRlIHtcbi8vICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAncHVibGljLWNoaWxkJyB9KVxuLy8gICBhY2Nlc3NvciBwdWJsaWNQcm9wO1xuXG4vLyAgIC8vIENoaWxkIGNhbiBoYXZlIGl0cyBvd24gcHJpdmF0ZSBwcm9wZXJ0eSB3aXRoIHNhbWUgbmFtZSAtIG5vIGNvbGxpc2lvbiFcbi8vICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAncHJpdmF0ZS1jaGlsZCcgfSlcbi8vICAgYWNjZXNzb3IgI3ByaXZhdGVQcm9wO1xuXG4vLyAgIGdldENoaWxkUHJpdmF0ZSgpIHtcbi8vICAgICByZXR1cm4gdGhpcy4jcHJpdmF0ZVByb3A7XG4vLyAgIH1cblxuLy8gICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuLy8gICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0LnB1YmxpY1Byb3B9PC9kaXY+YDtcbi8vICAgfVxuLy8gfVxuXG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtcGFyZW50LWVsZW1lbnQnLCBQYXJlbnRFbGVtZW50KTtcbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1jaGlsZC1lbGVtZW50JywgQ2hpbGRFbGVtZW50KTtcbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1wYXJlbnQtd2l0aC1saXN0ZW5lcicsIFBhcmVudFdpdGhMaXN0ZW5lcik7XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtY2hpbGQtd2l0aC1saXN0ZW5lcicsIENoaWxkV2l0aExpc3RlbmVyKTtcbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1ncmFuZHBhcmVudC1sZXZlbCcsIEdyYW5kcGFyZW50TGV2ZWwpO1xuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LXBhcmVudC1sZXZlbCcsIFBhcmVudExldmVsKTtcbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1jaGlsZC1sZXZlbCcsIENoaWxkTGV2ZWwpO1xuLy8gVE9ETzogQmFiZWwgZG9lc24ndCBzdXBwb3J0IGRlY29yYXRvcnMgb24gcHJpdmF0ZSBhY2Nlc3NvcnMgeWV0XG4vLyBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtcGFyZW50LXdpdGgtcHJpdmF0ZScsIFBhcmVudFdpdGhQcml2YXRlKTtcbi8vIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1jaGlsZC13aXRoLXByaXZhdGUnLCBDaGlsZFdpdGhQcml2YXRlKTtcblxuaXQoJ2NoaWxkIGluaGVyaXRzIHBhcmVudCBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtZWxlbWVudCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG4gIC8vIENoaWxkIHNob3VsZCBoYXZlIGJvdGggcGFyZW50IGFuZCBjaGlsZCBwcm9wZXJ0aWVzXG4gIGFzc2VydChjaGlsZC5wYXJlbnRQcm9wID09PSAncGFyZW50LXZhbHVlJywgJ2hhcyBwYXJlbnQgcHJvcGVydHknKTtcbiAgYXNzZXJ0KGNoaWxkLmNoaWxkUHJvcCA9PT0gJ2NoaWxkLXZhbHVlJywgJ2hhcyBjaGlsZCBwcm9wZXJ0eScpO1xuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgnY2hpbGQgY2FuIG92ZXJyaWRlIHBhcmVudCBwcm9wZXJ0aWVzJywgKCkgPT4ge1xuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtZWxlbWVudCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG4gIC8vIENoaWxkIG92ZXJyaWRlcyBzaGFyZWRQcm9wIHdpdGggaW5pdGlhbCB2YWx1ZSBvZiAxMDAgaW5zdGVhZCBvZiA0MlxuICBhc3NlcnQoY2hpbGQuc2hhcmVkUHJvcCA9PT0gMTAwLCAnY2hpbGQgb3ZlcnJpZGVzIHBhcmVudCBwcm9wZXJ0eScpO1xuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgncGFyZW50IHByb3BlcnRpZXMgcmVtYWluIGluZGVwZW5kZW50JywgKCkgPT4ge1xuICBjb25zdCBwYXJlbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LXBhcmVudC1lbGVtZW50Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKHBhcmVudCk7XG4gIC8vIFBhcmVudCBzaG91bGQgbm90IGJlIGFmZmVjdGVkIGJ5IGNoaWxkJ3Mgb3ZlcnJpZGVcbiAgYXNzZXJ0KHBhcmVudC5zaGFyZWRQcm9wID09PSA0MiwgJ3BhcmVudCBwcm9wZXJ0eSB1bmNoYW5nZWQnKTtcbiAgYXNzZXJ0KHBhcmVudC5jaGlsZFByb3AgPT09IHVuZGVmaW5lZCwgJ3BhcmVudCBkb2VzIG5vdCBoYXZlIGNoaWxkIHByb3BlcnR5Jyk7XG4gIHBhcmVudC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgnY2hpbGQgaW5oZXJpdHMgcGFyZW50IHRlbXBsYXRlIGJlaGF2aW9yJywgKCkgPT4ge1xuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtZWxlbWVudCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG4gIGNvbnN0IHRleHQgPSBjaGlsZC5zaGFkb3dSb290LnRleHRDb250ZW50O1xuICBhc3NlcnQodGV4dCA9PT0gJ3BhcmVudC12YWx1ZS1jaGlsZC12YWx1ZS0xMDAnLCAnY2hpbGQgcmVuZGVycyB3aXRoIGFsbCBwcm9wZXJ0aWVzJyk7XG4gIGNoaWxkLnJlbW92ZSgpO1xufSk7XG5cbml0KCdjaGlsZCBpbmhlcml0cyBwYXJlbnQgbGlzdGVuZXJzJywgKCkgPT4ge1xuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtd2l0aC1saXN0ZW5lcicpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG5cbiAgLy8gQ2hpbGQgc2hvdWxkIHJlc3BvbmQgdG8gYm90aCBwYXJlbnQgYW5kIGNoaWxkIGV2ZW50c1xuICBhc3NlcnQoY2hpbGQucGFyZW50Q2xpY2tzID09PSAwLCAncGFyZW50IGNsaWNrcyBzdGFydHMgYXQgMCcpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRDbGlja3MgPT09IDAsICdjaGlsZCBjbGlja3Mgc3RhcnRzIGF0IDAnKTtcblxuICAvLyBFdmVudHMgbXVzdCBiZSBkaXNwYXRjaGVkIGZyb20gd2l0aGluIHNoYWRvdyByb290IHNpbmNlIGxpc3RlbmVycyBhdHRhY2ggdGhlcmVcbiAgY2hpbGQuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgncGFyZW50LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGNoaWxkLnBhcmVudENsaWNrcyA9PT0gMSwgJ3BhcmVudCBsaXN0ZW5lciB3b3JrcyBpbiBjaGlsZCcpO1xuXG4gIGNoaWxkLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ2NoaWxkLWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGNoaWxkLmNoaWxkQ2xpY2tzID09PSAxLCAnY2hpbGQgbGlzdGVuZXIgd29ya3MnKTtcblxuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgncGFyZW50IGxpc3RlbmVycyByZW1haW4gaW5kZXBlbmRlbnQnLCAoKSA9PiB7XG4gIGNvbnN0IHBhcmVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtcGFyZW50LXdpdGgtbGlzdGVuZXInKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQocGFyZW50KTtcblxuICAvLyBFdmVudHMgbXVzdCBiZSBkaXNwYXRjaGVkIGZyb20gd2l0aGluIHNoYWRvdyByb290IHNpbmNlIGxpc3RlbmVycyBhdHRhY2ggdGhlcmVcbiAgcGFyZW50LnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ3BhcmVudC1ldmVudCcsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG4gIGFzc2VydChwYXJlbnQucGFyZW50Q2xpY2tzID09PSAxLCAncGFyZW50IGxpc3RlbmVyIHdvcmtzJyk7XG5cbiAgLy8gUGFyZW50IHNob3VsZCBub3QgcmVzcG9uZCB0byBjaGlsZC1vbmx5IGV2ZW50c1xuICBwYXJlbnQuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgnY2hpbGQtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQocGFyZW50LmNoaWxkQ2xpY2tzID09PSB1bmRlZmluZWQsICdwYXJlbnQgZG9lcyBub3QgaGF2ZSBjaGlsZCBsaXN0ZW5lcicpO1xuXG4gIHBhcmVudC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgnbXVsdGktbGV2ZWwgaW5oZXJpdGFuY2Ugd29ya3MgKGdyYW5kcGFyZW50IOKGkiBwYXJlbnQg4oaSIGNoaWxkKScsICgpID0+IHtcbiAgY29uc3QgZ3JhbmRwYXJlbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWdyYW5kcGFyZW50LWxldmVsJyk7XG4gIGNvbnN0IHBhcmVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtcGFyZW50LWxldmVsJyk7XG4gIGNvbnN0IGNoaWxkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1jaGlsZC1sZXZlbCcpO1xuXG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGdyYW5kcGFyZW50LCBwYXJlbnQsIGNoaWxkKTtcblxuICBhc3NlcnQoZ3JhbmRwYXJlbnQubGV2ZWwgPT09ICdncmFuZHBhcmVudCcsICdncmFuZHBhcmVudCBoYXMgY29ycmVjdCBsZXZlbCcpO1xuICBhc3NlcnQoZ3JhbmRwYXJlbnQuZ2VuZXJhdGlvbiA9PT0gMSwgJ2dyYW5kcGFyZW50IGhhcyBjb3JyZWN0IGdlbmVyYXRpb24nKTtcblxuICBhc3NlcnQocGFyZW50LmxldmVsID09PSAncGFyZW50JywgJ3BhcmVudCBvdmVycmlkZXMgbGV2ZWwnKTtcbiAgYXNzZXJ0KHBhcmVudC5nZW5lcmF0aW9uID09PSAyLCAncGFyZW50IG92ZXJyaWRlcyBnZW5lcmF0aW9uJyk7XG5cbiAgYXNzZXJ0KGNoaWxkLmxldmVsID09PSAnY2hpbGQnLCAnY2hpbGQgb3ZlcnJpZGVzIGxldmVsJyk7XG4gIGFzc2VydChjaGlsZC5nZW5lcmF0aW9uID09PSAzLCAnY2hpbGQgb3ZlcnJpZGVzIGdlbmVyYXRpb24nKTtcblxuICBncmFuZHBhcmVudC5yZW1vdmUoKTtcbiAgcGFyZW50LnJlbW92ZSgpO1xuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuXG5pdC5za2lwKCdwcml2YXRlIHByb3BlcnRpZXMgZG8gbm90IGNvbGxpZGUgYWNyb3NzIGluaGVyaXRhbmNlJywgKCkgPT4ge1xuICBjb25zdCBwYXJlbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LXBhcmVudC13aXRoLXByaXZhdGUnKTtcbiAgY29uc3QgY2hpbGQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWNoaWxkLXdpdGgtcHJpdmF0ZScpO1xuXG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKHBhcmVudCwgY2hpbGQpO1xuXG4gIC8vIEVhY2ggY2xhc3MgaGFzIGl0cyBvd24gcHJpdmF0ZSAjcHJpdmF0ZVByb3Agd2l0aCBkaWZmZXJlbnQgdmFsdWVzXG4gIGFzc2VydChwYXJlbnQuZ2V0UHJpdmF0ZSgpID09PSAncHJpdmF0ZS1wYXJlbnQnLCAncGFyZW50IGhhcyBpdHMgcHJpdmF0ZSB2YWx1ZScpO1xuICBhc3NlcnQoY2hpbGQuZ2V0UHJpdmF0ZSgpID09PSAncHJpdmF0ZS1wYXJlbnQnLCAnY2hpbGQgaW5oZXJpdHMgcGFyZW50IGdldHRlcicpO1xuICBhc3NlcnQoY2hpbGQuZ2V0Q2hpbGRQcml2YXRlKCkgPT09ICdwcml2YXRlLWNoaWxkJywgJ2NoaWxkIGhhcyBpdHMgb3duIHByaXZhdGUgdmFsdWUnKTtcblxuICAvLyBQdWJsaWMgcHJvcGVydGllcyBvdmVycmlkZSBjb3JyZWN0bHlcbiAgYXNzZXJ0KHBhcmVudC5wdWJsaWNQcm9wID09PSAncHVibGljLXBhcmVudCcsICdwYXJlbnQgcHVibGljIHByb3AnKTtcbiAgYXNzZXJ0KGNoaWxkLnB1YmxpY1Byb3AgPT09ICdwdWJsaWMtY2hpbGQnLCAnY2hpbGQgb3ZlcnJpZGVzIHB1YmxpYyBwcm9wJyk7XG5cbiAgcGFyZW50LnJlbW92ZSgpO1xuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgncHJvcGVydHkgbW9kaWZpY2F0aW9ucyBkbyBub3QgYWZmZWN0IHBhcmVudCBjbGFzcycsICgpID0+IHtcbiAgY29uc3QgcGFyZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1wYXJlbnQtZWxlbWVudCcpO1xuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtZWxlbWVudCcpO1xuXG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKHBhcmVudCwgY2hpbGQpO1xuXG4gIC8vIE1vZGlmeSBjaGlsZCBpbnN0YW5jZVxuICBjaGlsZC5wYXJlbnRQcm9wID0gJ21vZGlmaWVkJztcbiAgY2hpbGQuc2hhcmVkUHJvcCA9IDk5OTtcblxuICAvLyBQYXJlbnQgaW5zdGFuY2Ugc2hvdWxkIGJlIHVuYWZmZWN0ZWRcbiAgYXNzZXJ0KHBhcmVudC5wYXJlbnRQcm9wID09PSAncGFyZW50LXZhbHVlJywgJ3BhcmVudCBpbnN0YW5jZSB1bmNoYW5nZWQnKTtcbiAgYXNzZXJ0KHBhcmVudC5zaGFyZWRQcm9wID09PSA0MiwgJ3BhcmVudCBpbnN0YW5jZSB1bmNoYW5nZWQnKTtcblxuICAvLyBDcmVhdGUgbmV3IHBhcmVudCB0byB2ZXJpZnkgY2xhc3MgZGVmaW5pdGlvbiB1bmNoYW5nZWRcbiAgY29uc3QgcGFyZW50MiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtcGFyZW50LWVsZW1lbnQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQocGFyZW50Mik7XG4gIGFzc2VydChwYXJlbnQyLnBhcmVudFByb3AgPT09ICdwYXJlbnQtdmFsdWUnLCAncGFyZW50IGNsYXNzIGRlZmluaXRpb24gdW5jaGFuZ2VkJyk7XG4gIGFzc2VydChwYXJlbnQyLnNoYXJlZFByb3AgPT09IDQyLCAncGFyZW50IGNsYXNzIGRlZmluaXRpb24gdW5jaGFuZ2VkJyk7XG5cbiAgcGFyZW50LnJlbW92ZSgpO1xuICBjaGlsZC5yZW1vdmUoKTtcbiAgcGFyZW50Mi5yZW1vdmUoKTtcbn0pO1xuXG5pdCgnb3V0LW9mLW9yZGVyIGFuYWx5c2lzIChjaGlsZCBhbmFseXplZCBiZWZvcmUgcGFyZW50KScsICgpID0+IHtcbiAgLy8gRGVmaW5lIG5ldyBwYXJlbnQgYW5kIGNoaWxkIGNsYXNzZXMgaW5saW5lIHRvIGVuc3VyZSBmcmVzaCBhbmFseXNpc1xuICBjbGFzcyBPdXRPZk9yZGVyUGFyZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogJ3BhcmVudC1vb28nIH0pXG4gICAgYWNjZXNzb3IgcGFyZW50VmFsdWU7XG5cbiAgICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDEwIH0pXG4gICAgYWNjZXNzb3Igc2hhcmVkVmFsdWU7XG4gIH1cblxuICBjbGFzcyBPdXRPZk9yZGVyQ2hpbGQgZXh0ZW5kcyBPdXRPZk9yZGVyUGFyZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdjaGlsZC1vb28nIH0pXG4gICAgYWNjZXNzb3IgY2hpbGRWYWx1ZTtcblxuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciwgaW5pdGlhbDogMjAgfSlcbiAgICBhY2Nlc3NvciBzaGFyZWRWYWx1ZTtcbiAgfVxuXG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1vb28tcGFyZW50JywgT3V0T2ZPcmRlclBhcmVudCk7XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1vb28tY2hpbGQnLCBPdXRPZk9yZGVyQ2hpbGQpO1xuXG4gIC8vIENyZWF0ZSBjaGlsZCBGSVJTVCAtIHRoaXMgdHJpZ2dlcnMgYW5hbHlzaXMgb2YgY2hpbGQsIHdoaWNoIHNob3VsZFxuICAvLyBhdXRvbWF0aWNhbGx5IHRyaWdnZXIgcGFyZW50IGFuYWx5c2lzIGZpcnN0IHRvIGVuc3VyZSBwcm9wZXIgaW5oZXJpdGFuY2VcbiAgY29uc3QgY2hpbGQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LW9vby1jaGlsZCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG5cbiAgLy8gVmVyaWZ5IGNoaWxkIGhhcyBjb3JyZWN0IHZhbHVlcyAod291bGQgZmFpbCBpZiBwYXJlbnQgd2Fzbid0IGFuYWx5emVkIGZpcnN0KVxuICBhc3NlcnQoY2hpbGQucGFyZW50VmFsdWUgPT09ICdwYXJlbnQtb29vJywgJ2NoaWxkIGluaGVyaXRzIHBhcmVudCBwcm9wZXJ0eScpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRWYWx1ZSA9PT0gJ2NoaWxkLW9vbycsICdjaGlsZCBoYXMgaXRzIG93biBwcm9wZXJ0eScpO1xuICBhc3NlcnQoY2hpbGQuc2hhcmVkVmFsdWUgPT09IDIwLCAnY2hpbGQgb3ZlcnJpZGVzIHBhcmVudCBwcm9wZXJ0eScpO1xuXG4gIC8vIE5vdyBjcmVhdGUgcGFyZW50IGFuZCB2ZXJpZnkgaXQncyB1bmFmZmVjdGVkIGJ5IGNoaWxkXG4gIGNvbnN0IHBhcmVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3Qtb29vLXBhcmVudCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChwYXJlbnQpO1xuXG4gIGFzc2VydChwYXJlbnQucGFyZW50VmFsdWUgPT09ICdwYXJlbnQtb29vJywgJ3BhcmVudCBwcm9wZXJ0eSBjb3JyZWN0Jyk7XG4gIGFzc2VydChwYXJlbnQuc2hhcmVkVmFsdWUgPT09IDEwLCAncGFyZW50IHByb3BlcnR5IG5vdCBhZmZlY3RlZCBieSBjaGlsZCcpO1xuICBhc3NlcnQocGFyZW50LmNoaWxkVmFsdWUgPT09IHVuZGVmaW5lZCwgJ3BhcmVudCBkb2VzIG5vdCBoYXZlIGNoaWxkIHByb3BlcnR5Jyk7XG5cbiAgY2hpbGQucmVtb3ZlKCk7XG4gIHBhcmVudC5yZW1vdmUoKTtcbn0pO1xuXG5pdCgncGFyZW50IGFuZCBjaGlsZCBib3RoIGxpc3RlbiB0byBzYW1lIGV2ZW50IHR5cGUnLCAoKSA9PiB7XG4gIC8vIFBhcmVudCBhbmQgY2hpbGQgZWFjaCBoYXZlIGRpZmZlcmVudCBtZXRob2RzIGxpc3RlbmluZyB0byBzYW1lIGV2ZW50IHR5cGVcbiAgY2xhc3MgUGFyZW50TXVsdGlMaXN0ZW5lciBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDAgfSlcbiAgICBhY2Nlc3NvciBwYXJlbnRDb3VudDtcblxuICAgIEBsaXN0ZW5lcignc2hhcmVkLWV2ZW50JylcbiAgICBzdGF0aWMgb25QYXJlbnRTaGFyZWQoaG9zdCkge1xuICAgICAgaG9zdC5wYXJlbnRDb3VudCsrO1xuICAgIH1cblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICByZXR1cm4gaHRtbGA8ZGl2PnBhcmVudDogJHtob3N0LnBhcmVudENvdW50fTwvZGl2PmA7XG4gICAgfVxuICB9XG5cbiAgY2xhc3MgQ2hpbGRNdWx0aUxpc3RlbmVyIGV4dGVuZHMgUGFyZW50TXVsdGlMaXN0ZW5lciB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gICAgYWNjZXNzb3IgY2hpbGRDb3VudDtcblxuICAgIEBsaXN0ZW5lcignc2hhcmVkLWV2ZW50JylcbiAgICBzdGF0aWMgb25DaGlsZFNoYXJlZChob3N0KSB7XG4gICAgICBob3N0LmNoaWxkQ291bnQrKztcbiAgICB9XG5cbiAgICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgICAgcmV0dXJuIGh0bWxgPGRpdj5wYXJlbnQ6ICR7aG9zdC5wYXJlbnRDb3VudH0sIGNoaWxkOiAke2hvc3QuY2hpbGRDb3VudH08L2Rpdj5gO1xuICAgIH1cbiAgfVxuXG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1wYXJlbnQtbXVsdGknLCBQYXJlbnRNdWx0aUxpc3RlbmVyKTtcbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWNoaWxkLW11bHRpJywgQ2hpbGRNdWx0aUxpc3RlbmVyKTtcblxuICBjb25zdCBjaGlsZCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtY2hpbGQtbXVsdGknKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoY2hpbGQpO1xuXG4gIGFzc2VydChjaGlsZC5wYXJlbnRDb3VudCA9PT0gMCwgJ3BhcmVudCBjb3VudCBzdGFydHMgYXQgMCcpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRDb3VudCA9PT0gMCwgJ2NoaWxkIGNvdW50IHN0YXJ0cyBhdCAwJyk7XG5cbiAgLy8gRGlzcGF0Y2ggc2hhcmVkIGV2ZW50IC0gYm90aCBwYXJlbnQgYW5kIGNoaWxkIGxpc3RlbmVycyBzaG91bGQgZmlyZVxuICBjaGlsZC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCdzaGFyZWQtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuXG4gIGFzc2VydChjaGlsZC5wYXJlbnRDb3VudCA9PT0gMSwgJ3BhcmVudCBsaXN0ZW5lciBmaXJlZCcpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRDb3VudCA9PT0gMSwgJ2NoaWxkIGxpc3RlbmVyIGZpcmVkJyk7XG5cbiAgLy8gRmlyZSBhZ2FpbiAtIGJvdGggc2hvdWxkIGluY3JlbWVudFxuICBjaGlsZC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCdzaGFyZWQtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuXG4gIGFzc2VydChjaGlsZC5wYXJlbnRDb3VudCA9PT0gMiwgJ3BhcmVudCBsaXN0ZW5lciBmaXJlZCBhZ2FpbicpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRDb3VudCA9PT0gMiwgJ2NoaWxkIGxpc3RlbmVyIGZpcmVkIGFnYWluJyk7XG5cbiAgLy8gVmVyaWZ5IHBhcmVudCBpbnN0YW5jZSBvbmx5IGhhcyBwYXJlbnQgbGlzdGVuZXJcbiAgY29uc3QgcGFyZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1wYXJlbnQtbXVsdGknKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQocGFyZW50KTtcblxuICBwYXJlbnQuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgnc2hhcmVkLWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcblxuICBhc3NlcnQocGFyZW50LnBhcmVudENvdW50ID09PSAxLCAncGFyZW50IGxpc3RlbmVyIHdvcmtzIG9uIHBhcmVudCBpbnN0YW5jZScpO1xuICBhc3NlcnQocGFyZW50LmNoaWxkQ291bnQgPT09IHVuZGVmaW5lZCwgJ3BhcmVudCBpbnN0YW5jZSBoYXMgbm8gY2hpbGQgbGlzdGVuZXInKTtcblxuICBjaGlsZC5yZW1vdmUoKTtcbiAgcGFyZW50LnJlbW92ZSgpO1xufSk7XG5cbml0KCdjaGlsZCBvdmVycmlkZXMgcGFyZW50IGxpc3RlbmVyIHdpdGggc2FtZSBtZXRob2QgbmFtZScsICgpID0+IHtcbiAgLy8gV2hlbiBjaGlsZCByZWRlZmluZXMgYSBtZXRob2Qgd2l0aCB0aGUgc2FtZSBuYW1lLCBvbmx5IGNoaWxkJ3MgbGlzdGVuZXIgZmlyZXNcbiAgY2xhc3MgUGFyZW50V2l0aE1ldGhvZCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDAgfSlcbiAgICBhY2Nlc3NvciBwYXJlbnRDbGlja3M7XG5cbiAgICBAbGlzdGVuZXIoJ2NsaWNrJylcbiAgICBzdGF0aWMgb25DbGljayhob3N0KSB7XG4gICAgICBob3N0LnBhcmVudENsaWNrcysrO1xuICAgIH1cblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICByZXR1cm4gaHRtbGA8ZGl2PnBhcmVudDogJHtob3N0LnBhcmVudENsaWNrc308L2Rpdj5gO1xuICAgIH1cbiAgfVxuXG4gIGNsYXNzIENoaWxkT3ZlcnJpZGVzTWV0aG9kIGV4dGVuZHMgUGFyZW50V2l0aE1ldGhvZCB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gICAgYWNjZXNzb3IgY2hpbGRDbGlja3M7XG5cbiAgICBAbGlzdGVuZXIoJ2NsaWNrJylcbiAgICBzdGF0aWMgb25DbGljayhob3N0KSB7XG4gICAgICAvLyBTYW1lIG1ldGhvZCBuYW1lIGFzIHBhcmVudCAtIHNob3VsZCBvdmVycmlkZVxuICAgICAgaG9zdC5jaGlsZENsaWNrcysrO1xuICAgIH1cblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICByZXR1cm4gaHRtbGA8ZGl2PnBhcmVudDogJHtob3N0LnBhcmVudENsaWNrc30sIGNoaWxkOiAke2hvc3QuY2hpbGRDbGlja3N9PC9kaXY+YDtcbiAgICB9XG4gIH1cblxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtcGFyZW50LXdpdGgtbWV0aG9kJywgUGFyZW50V2l0aE1ldGhvZCk7XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1jaGlsZC1vdmVycmlkZXMtbWV0aG9kJywgQ2hpbGRPdmVycmlkZXNNZXRob2QpO1xuXG4gIGNvbnN0IGNoaWxkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1jaGlsZC1vdmVycmlkZXMtbWV0aG9kJyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGNoaWxkKTtcblxuICBhc3NlcnQoY2hpbGQucGFyZW50Q2xpY2tzID09PSAwLCAncGFyZW50IGNsaWNrcyBzdGFydHMgYXQgMCcpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRDbGlja3MgPT09IDAsICdjaGlsZCBjbGlja3Mgc3RhcnRzIGF0IDAnKTtcblxuICAvLyBEaXNwYXRjaCBjbGljayBldmVudCAtIG9ubHkgY2hpbGQncyBvbkNsaWNrIHNob3VsZCBmaXJlXG4gIGNoaWxkLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ2NsaWNrJywgeyBidWJibGVzOiB0cnVlIH0pKTtcblxuICBhc3NlcnQoY2hpbGQucGFyZW50Q2xpY2tzID09PSAwLCAncGFyZW50IGxpc3RlbmVyIGRpZCBub3QgZmlyZSAob3ZlcnJpZGRlbiBieSBjaGlsZCknKTtcbiAgYXNzZXJ0KGNoaWxkLmNoaWxkQ2xpY2tzID09PSAxLCAnY2hpbGQgbGlzdGVuZXIgZmlyZWQnKTtcblxuICAvLyBWZXJpZnkgcGFyZW50IGluc3RhbmNlIHVzZXMgcGFyZW50J3Mgb25DbGlja1xuICBjb25zdCBwYXJlbnQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LXBhcmVudC13aXRoLW1ldGhvZCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChwYXJlbnQpO1xuXG4gIHBhcmVudC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCdjbGljaycsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG5cbiAgYXNzZXJ0KHBhcmVudC5wYXJlbnRDbGlja3MgPT09IDEsICdwYXJlbnQgbGlzdGVuZXIgd29ya3Mgb24gcGFyZW50IGluc3RhbmNlJyk7XG4gIGFzc2VydChwYXJlbnQuY2hpbGRDbGlja3MgPT09IHVuZGVmaW5lZCwgJ3BhcmVudCBpbnN0YW5jZSBoYXMgbm8gY2hpbGQgcHJvcGVydHknKTtcblxuICBjaGlsZC5yZW1vdmUoKTtcbiAgcGFyZW50LnJlbW92ZSgpO1xufSk7XG5cbml0KCdwbGFpbiBjbGFzcyB3aXRob3V0IGRlY29yYXRvcnMgKG1pc3NpbmcgbWV0YWRhdGEgbGluaykgd29ya3MgY29ycmVjdGx5JywgKCkgPT4ge1xuICAvLyBUZXN0IHRoYXQgYSBjbGFzcyB3aXRob3V0IGRlY29yYXRvcnMgaW4gdGhlIG1pZGRsZSBvZiB0aGUgaW5oZXJpdGFuY2UgY2hhaW5cbiAgLy8gd29ya3MgY29ycmVjdGx5IGFuZCBkb2Vzbid0IGhhdmUgU3ltYm9sLm1ldGFkYXRhIHBvbGx1dGVkIG9udG8gaXRcblxuICBjbGFzcyBEZWNvcmF0ZWRCYXNlIGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogJ2Jhc2UtdmFsdWUnIH0pXG4gICAgYWNjZXNzb3IgYmFzZVByb3A7XG5cbiAgICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDEgfSlcbiAgICBhY2Nlc3NvciBnZW5lcmF0aW9uO1xuICB9XG5cbiAgLy8gUGxhaW4gY2xhc3Mgd2l0aCBOTyBkZWNvcmF0b3JzIC0gdGhpcyBpcyB0aGUgXCJtaXNzaW5nIGxpbmtcIlxuICBjbGFzcyBQbGFpbk1pZGRsZSBleHRlbmRzIERlY29yYXRlZEJhc2Uge1xuICAgIC8vIE5vIGRlY29yYXRvcnMhIEp1c3QgcGxhaW4gY2xhc3MgZXh0ZW5kaW5nIGRlY29yYXRlZCBiYXNlXG4gIH1cblxuICBjbGFzcyBEZWNvcmF0ZWRDaGlsZCBleHRlbmRzIFBsYWluTWlkZGxlIHtcbiAgICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICdjaGlsZC12YWx1ZScgfSlcbiAgICBhY2Nlc3NvciBjaGlsZFByb3A7XG5cbiAgICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDMgfSlcbiAgICBhY2Nlc3NvciBnZW5lcmF0aW9uO1xuICB9XG5cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWRlY29yYXRlZC1iYXNlLWxpbmsnLCBEZWNvcmF0ZWRCYXNlKTtcbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LXBsYWluLW1pZGRsZS1saW5rJywgUGxhaW5NaWRkbGUpO1xuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZGVjb3JhdGVkLWNoaWxkLWxpbmsnLCBEZWNvcmF0ZWRDaGlsZCk7XG5cbiAgLy8gQ1JJVElDQUw6IFZlcmlmeSBQbGFpbk1pZGRsZSBkb2Vzbid0IGhhdmUgU3ltYm9sLm1ldGFkYXRhIGFzIE9XTiBwcm9wZXJ0eVxuICAvLyAoaXQgbWlnaHQgaW5oZXJpdCBmcm9tIHBhcmVudCB2aWEgcHJvdG90eXBlIGNoYWluLCBidXQgc2hvdWxkbid0IGhhdmUgaXRzIG93bilcbiAgYXNzZXJ0KCFPYmplY3QuaGFzT3duKFBsYWluTWlkZGxlLCBTeW1ib2wubWV0YWRhdGEpLCAncGxhaW4gY2xhc3MgaGFzIG5vIG93biBTeW1ib2wubWV0YWRhdGEgcHJvcGVydHknKTtcblxuICAvLyBWZXJpZnkgRGVjb3JhdGVkQmFzZSBhbmQgRGVjb3JhdGVkQ2hpbGQgRE8gaGF2ZSBTeW1ib2wubWV0YWRhdGEgKGZyb20gZGVjb3JhdG9ycylcbiAgYXNzZXJ0KERlY29yYXRlZEJhc2VbU3ltYm9sLm1ldGFkYXRhXSAhPT0gdW5kZWZpbmVkLCAnZGVjb3JhdGVkIGJhc2UgaGFzIFN5bWJvbC5tZXRhZGF0YScpO1xuICBhc3NlcnQoRGVjb3JhdGVkQ2hpbGRbU3ltYm9sLm1ldGFkYXRhXSAhPT0gdW5kZWZpbmVkLCAnZGVjb3JhdGVkIGNoaWxkIGhhcyBTeW1ib2wubWV0YWRhdGEnKTtcblxuICAvLyBDcmVhdGUgaW5zdGFuY2VzIGFuZCB2ZXJpZnkgaW5oZXJpdGFuY2Ugc3RpbGwgd29ya3MgY29ycmVjdGx5XG4gIGNvbnN0IGJhc2UgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWRlY29yYXRlZC1iYXNlLWxpbmsnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoYmFzZSk7XG5cbiAgYXNzZXJ0KGJhc2UuYmFzZVByb3AgPT09ICdiYXNlLXZhbHVlJywgJ2Jhc2UgaGFzIGJhc2VQcm9wJyk7XG4gIGFzc2VydChiYXNlLmdlbmVyYXRpb24gPT09IDEsICdiYXNlIGhhcyBnZW5lcmF0aW9uIDEnKTtcbiAgYXNzZXJ0KGJhc2UuY2hpbGRQcm9wID09PSB1bmRlZmluZWQsICdiYXNlIGhhcyBubyBjaGlsZCBwcm9wZXJ0eScpO1xuXG4gIGNvbnN0IG1pZGRsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtcGxhaW4tbWlkZGxlLWxpbmsnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQobWlkZGxlKTtcblxuICBhc3NlcnQobWlkZGxlLmJhc2VQcm9wID09PSAnYmFzZS12YWx1ZScsICdtaWRkbGUgaW5oZXJpdHMgYmFzZVByb3AgZnJvbSBiYXNlJyk7XG4gIGFzc2VydChtaWRkbGUuZ2VuZXJhdGlvbiA9PT0gMSwgJ21pZGRsZSBpbmhlcml0cyBnZW5lcmF0aW9uIGZyb20gYmFzZScpO1xuICBhc3NlcnQobWlkZGxlLmNoaWxkUHJvcCA9PT0gdW5kZWZpbmVkLCAnbWlkZGxlIGhhcyBubyBjaGlsZCBwcm9wZXJ0eScpO1xuXG4gIGNvbnN0IGNoaWxkID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1kZWNvcmF0ZWQtY2hpbGQtbGluaycpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChjaGlsZCk7XG5cbiAgYXNzZXJ0KGNoaWxkLmJhc2VQcm9wID09PSAnYmFzZS12YWx1ZScsICdjaGlsZCBpbmhlcml0cyBiYXNlUHJvcCB0aHJvdWdoIHBsYWluIG1pZGRsZScpO1xuICBhc3NlcnQoY2hpbGQuY2hpbGRQcm9wID09PSAnY2hpbGQtdmFsdWUnLCAnY2hpbGQgaGFzIGNoaWxkUHJvcCcpO1xuICBhc3NlcnQoY2hpbGQuZ2VuZXJhdGlvbiA9PT0gMywgJ2NoaWxkIG92ZXJyaWRlcyBnZW5lcmF0aW9uJyk7XG5cbiAgLy8gVmVyaWZ5IHRoYXQgbW9kaWZpY2F0aW9ucyB0byBjaGlsZCBkb24ndCBhZmZlY3QgbWlkZGxlIG9yIGJhc2VcbiAgY2hpbGQuYmFzZVByb3AgPSAnbW9kaWZpZWQnO1xuICBhc3NlcnQobWlkZGxlLmJhc2VQcm9wID09PSAnYmFzZS12YWx1ZScsICdtaWRkbGUgdW5jaGFuZ2VkIGFmdGVyIGNoaWxkIG1vZGlmaWNhdGlvbicpO1xuICBhc3NlcnQoYmFzZS5iYXNlUHJvcCA9PT0gJ2Jhc2UtdmFsdWUnLCAnYmFzZSB1bmNoYW5nZWQgYWZ0ZXIgY2hpbGQgbW9kaWZpY2F0aW9uJyk7XG5cbiAgYmFzZS5yZW1vdmUoKTtcbiAgbWlkZGxlLnJlbW92ZSgpO1xuICBjaGlsZC5yZW1vdmUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxTQUFTQSxNQUFNLEVBQUVDLEVBQUUsUUFBUSwyQkFBMkI7QUFDdEQsU0FBU0MsUUFBUSxFQUFFQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsSUFBSSxRQUFRLHNCQUFzQjs7QUFFekU7QUFDQSxNQUFNQyxhQUFhLFNBQVNKLFFBQVEsQ0FBQztFQUFBO0lBQUEsQ0FBQUssZ0JBQUEsRUFBQUMsZ0JBQUEsRUFBQUMsVUFBQSxJQUFBQyxVQUFBLFNBQUFDLGVBQUEscUJBQUFDLGVBQUEsb0NBQVRWLFFBQVEsRUFBQVcsQ0FBQTtFQUFBO0VBQUEsQ0FBQUMsQ0FBQSxJQUFBTCxVQUFBLFFBQUFGLGdCQUFBO0VBQUEsTUFBQUksZUFBQSxHQUNqQ1IsUUFBUSxDQUFDO0lBQUVZLElBQUksRUFBRUMsTUFBTTtJQUFFQyxPQUFPLEVBQUU7RUFBZSxDQUFDLENBQUMsRUFBQUwsZUFBQSxHQUduRFQsUUFBUSxD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 \ No newline at end of file diff --git a/test-next/test-inheritance.src.js b/test-next/test-inheritance.src.js new file mode 100644 index 0000000..38b0195 --- /dev/null +++ b/test-next/test-inheritance.src.js @@ -0,0 +1,494 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener, html } from '../x-element-next.js'; + +// Test basic property inheritance +class ParentElement extends XElement { + @property({ type: String, initial: 'parent-value' }) + accessor parentProp; + + @property({ type: Number, initial: 42 }) + accessor sharedProp; + + static template(host) { + return html`
${host.parentProp}-${host.sharedProp}
`; + } +} + +class ChildElement extends ParentElement { + @property({ type: String, initial: 'child-value' }) + accessor childProp; + + // Override parent property with different configuration + @property({ type: Number, initial: 100 }) + accessor sharedProp; + + static template(host) { + return html`
${host.parentProp}-${host.childProp}-${host.sharedProp}
`; + } +} + +// Test listener inheritance +class ParentWithListener extends XElement { + @property({ type: Number, initial: 0 }) + accessor parentClicks; + + @listener('parent-event') + static onParentEvent(host) { + host.parentClicks++; + } + + static template(host) { + return html`
parent: ${host.parentClicks}
`; + } +} + +class ChildWithListener extends ParentWithListener { + @property({ type: Number, initial: 0 }) + accessor childClicks; + + @listener('child-event') + static onChildEvent(host) { + host.childClicks++; + } + + static template(host) { + return html`
parent: ${host.parentClicks}, child: ${host.childClicks}
`; + } +} + +// Test multi-level inheritance +class GrandparentLevel extends XElement { + @property({ type: String, initial: 'grandparent' }) + accessor level; + + @property({ type: Number, initial: 1 }) + accessor generation; +} + +class ParentLevel extends GrandparentLevel { + @property({ type: String, initial: 'parent' }) + accessor level; + + @property({ type: Number, initial: 2 }) + accessor generation; +} + +class ChildLevel extends ParentLevel { + @property({ type: String, initial: 'child' }) + accessor level; + + @property({ type: Number, initial: 3 }) + accessor generation; +} + +// Test private property inheritance +// class ParentWithPrivate extends XElement { +// @property({ type: String, initial: 'public-parent' }) +// accessor publicProp; + +// @property({ type: String, initial: 'private-parent' }) +// accessor #privateProp; + +// getPrivate() { +// return this.#privateProp; +// } + +// static template(host) { +// return html`
${host.publicProp}
`; +// } +// } + +// class ChildWithPrivate extends ParentWithPrivate { +// @property({ type: String, initial: 'public-child' }) +// accessor publicProp; + +// // Child can have its own private property with same name - no collision! +// @property({ type: String, initial: 'private-child' }) +// accessor #privateProp; + +// getChildPrivate() { +// return this.#privateProp; +// } + +// static template(host) { +// return html`
${host.publicProp}
`; +// } +// } + +customElements.define('test-parent-element', ParentElement); +customElements.define('test-child-element', ChildElement); +customElements.define('test-parent-with-listener', ParentWithListener); +customElements.define('test-child-with-listener', ChildWithListener); +customElements.define('test-grandparent-level', GrandparentLevel); +customElements.define('test-parent-level', ParentLevel); +customElements.define('test-child-level', ChildLevel); +// TODO: Babel doesn't support decorators on private accessors yet +// customElements.define('test-parent-with-private', ParentWithPrivate); +// customElements.define('test-child-with-private', ChildWithPrivate); + +it('child inherits parent properties', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + // Child should have both parent and child properties + assert(child.parentProp === 'parent-value', 'has parent property'); + assert(child.childProp === 'child-value', 'has child property'); + child.remove(); +}); + +it('child can override parent properties', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + // Child overrides sharedProp with initial value of 100 instead of 42 + assert(child.sharedProp === 100, 'child overrides parent property'); + child.remove(); +}); + +it('parent properties remain independent', () => { + const parent = document.createElement('test-parent-element'); + document.body.append(parent); + // Parent should not be affected by child's override + assert(parent.sharedProp === 42, 'parent property unchanged'); + assert(parent.childProp === undefined, 'parent does not have child property'); + parent.remove(); +}); + +it('child inherits parent template behavior', () => { + const child = document.createElement('test-child-element'); + document.body.append(child); + const text = child.shadowRoot.textContent; + assert(text === 'parent-value-child-value-100', 'child renders with all properties'); + child.remove(); +}); + +it('child inherits parent listeners', () => { + const child = document.createElement('test-child-with-listener'); + document.body.append(child); + + // Child should respond to both parent and child events + assert(child.parentClicks === 0, 'parent clicks starts at 0'); + assert(child.childClicks === 0, 'child clicks starts at 0'); + + // Events must be dispatched from within shadow root since listeners attach there + child.shadowRoot.dispatchEvent(new CustomEvent('parent-event', { bubbles: true })); + assert(child.parentClicks === 1, 'parent listener works in child'); + + child.shadowRoot.dispatchEvent(new CustomEvent('child-event', { bubbles: true })); + assert(child.childClicks === 1, 'child listener works'); + + child.remove(); +}); + +it('parent listeners remain independent', () => { + const parent = document.createElement('test-parent-with-listener'); + document.body.append(parent); + + // Events must be dispatched from within shadow root since listeners attach there + parent.shadowRoot.dispatchEvent(new CustomEvent('parent-event', { bubbles: true })); + assert(parent.parentClicks === 1, 'parent listener works'); + + // Parent should not respond to child-only events + parent.shadowRoot.dispatchEvent(new CustomEvent('child-event', { bubbles: true })); + assert(parent.childClicks === undefined, 'parent does not have child listener'); + + parent.remove(); +}); + +it('multi-level inheritance works (grandparent → parent → child)', () => { + const grandparent = document.createElement('test-grandparent-level'); + const parent = document.createElement('test-parent-level'); + const child = document.createElement('test-child-level'); + + document.body.append(grandparent, parent, child); + + assert(grandparent.level === 'grandparent', 'grandparent has correct level'); + assert(grandparent.generation === 1, 'grandparent has correct generation'); + + assert(parent.level === 'parent', 'parent overrides level'); + assert(parent.generation === 2, 'parent overrides generation'); + + assert(child.level === 'child', 'child overrides level'); + assert(child.generation === 3, 'child overrides generation'); + + grandparent.remove(); + parent.remove(); + child.remove(); +}); + +it.skip('private properties do not collide across inheritance', () => { + const parent = document.createElement('test-parent-with-private'); + const child = document.createElement('test-child-with-private'); + + document.body.append(parent, child); + + // Each class has its own private #privateProp with different values + assert(parent.getPrivate() === 'private-parent', 'parent has its private value'); + assert(child.getPrivate() === 'private-parent', 'child inherits parent getter'); + assert(child.getChildPrivate() === 'private-child', 'child has its own private value'); + + // Public properties override correctly + assert(parent.publicProp === 'public-parent', 'parent public prop'); + assert(child.publicProp === 'public-child', 'child overrides public prop'); + + parent.remove(); + child.remove(); +}); + +it('property modifications do not affect parent class', () => { + const parent = document.createElement('test-parent-element'); + const child = document.createElement('test-child-element'); + + document.body.append(parent, child); + + // Modify child instance + child.parentProp = 'modified'; + child.sharedProp = 999; + + // Parent instance should be unaffected + assert(parent.parentProp === 'parent-value', 'parent instance unchanged'); + assert(parent.sharedProp === 42, 'parent instance unchanged'); + + // Create new parent to verify class definition unchanged + const parent2 = document.createElement('test-parent-element'); + document.body.append(parent2); + assert(parent2.parentProp === 'parent-value', 'parent class definition unchanged'); + assert(parent2.sharedProp === 42, 'parent class definition unchanged'); + + parent.remove(); + child.remove(); + parent2.remove(); +}); + +it('out-of-order analysis (child analyzed before parent)', () => { + // Define new parent and child classes inline to ensure fresh analysis + class OutOfOrderParent extends XElement { + @property({ type: String, initial: 'parent-ooo' }) + accessor parentValue; + + @property({ type: Number, initial: 10 }) + accessor sharedValue; + } + + class OutOfOrderChild extends OutOfOrderParent { + @property({ type: String, initial: 'child-ooo' }) + accessor childValue; + + @property({ type: Number, initial: 20 }) + accessor sharedValue; + } + + customElements.define('test-ooo-parent', OutOfOrderParent); + customElements.define('test-ooo-child', OutOfOrderChild); + + // Create child FIRST - this triggers analysis of child, which should + // automatically trigger parent analysis first to ensure proper inheritance + const child = document.createElement('test-ooo-child'); + document.body.append(child); + + // Verify child has correct values (would fail if parent wasn't analyzed first) + assert(child.parentValue === 'parent-ooo', 'child inherits parent property'); + assert(child.childValue === 'child-ooo', 'child has its own property'); + assert(child.sharedValue === 20, 'child overrides parent property'); + + // Now create parent and verify it's unaffected by child + const parent = document.createElement('test-ooo-parent'); + document.body.append(parent); + + assert(parent.parentValue === 'parent-ooo', 'parent property correct'); + assert(parent.sharedValue === 10, 'parent property not affected by child'); + assert(parent.childValue === undefined, 'parent does not have child property'); + + child.remove(); + parent.remove(); +}); + +it('parent and child both listen to same event type', () => { + // Parent and child each have different methods listening to same event type + class ParentMultiListener extends XElement { + @property({ type: Number, initial: 0 }) + accessor parentCount; + + @listener('shared-event') + static onParentShared(host) { + host.parentCount++; + } + + static template(host) { + return html`
parent: ${host.parentCount}
`; + } + } + + class ChildMultiListener extends ParentMultiListener { + @property({ type: Number, initial: 0 }) + accessor childCount; + + @listener('shared-event') + static onChildShared(host) { + host.childCount++; + } + + static template(host) { + return html`
parent: ${host.parentCount}, child: ${host.childCount}
`; + } + } + + customElements.define('test-parent-multi', ParentMultiListener); + customElements.define('test-child-multi', ChildMultiListener); + + const child = document.createElement('test-child-multi'); + document.body.append(child); + + assert(child.parentCount === 0, 'parent count starts at 0'); + assert(child.childCount === 0, 'child count starts at 0'); + + // Dispatch shared event - both parent and child listeners should fire + child.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { bubbles: true })); + + assert(child.parentCount === 1, 'parent listener fired'); + assert(child.childCount === 1, 'child listener fired'); + + // Fire again - both should increment + child.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { bubbles: true })); + + assert(child.parentCount === 2, 'parent listener fired again'); + assert(child.childCount === 2, 'child listener fired again'); + + // Verify parent instance only has parent listener + const parent = document.createElement('test-parent-multi'); + document.body.append(parent); + + parent.shadowRoot.dispatchEvent(new CustomEvent('shared-event', { bubbles: true })); + + assert(parent.parentCount === 1, 'parent listener works on parent instance'); + assert(parent.childCount === undefined, 'parent instance has no child listener'); + + child.remove(); + parent.remove(); +}); + +it('child overrides parent listener with same method name', () => { + // When child redefines a method with the same name, only child's listener fires + class ParentWithMethod extends XElement { + @property({ type: Number, initial: 0 }) + accessor parentClicks; + + @listener('click') + static onClick(host) { + host.parentClicks++; + } + + static template(host) { + return html`
parent: ${host.parentClicks}
`; + } + } + + class ChildOverridesMethod extends ParentWithMethod { + @property({ type: Number, initial: 0 }) + accessor childClicks; + + @listener('click') + static onClick(host) { + // Same method name as parent - should override + host.childClicks++; + } + + static template(host) { + return html`
parent: ${host.parentClicks}, child: ${host.childClicks}
`; + } + } + + customElements.define('test-parent-with-method', ParentWithMethod); + customElements.define('test-child-overrides-method', ChildOverridesMethod); + + const child = document.createElement('test-child-overrides-method'); + document.body.append(child); + + assert(child.parentClicks === 0, 'parent clicks starts at 0'); + assert(child.childClicks === 0, 'child clicks starts at 0'); + + // Dispatch click event - only child's onClick should fire + child.shadowRoot.dispatchEvent(new CustomEvent('click', { bubbles: true })); + + assert(child.parentClicks === 0, 'parent listener did not fire (overridden by child)'); + assert(child.childClicks === 1, 'child listener fired'); + + // Verify parent instance uses parent's onClick + const parent = document.createElement('test-parent-with-method'); + document.body.append(parent); + + parent.shadowRoot.dispatchEvent(new CustomEvent('click', { bubbles: true })); + + assert(parent.parentClicks === 1, 'parent listener works on parent instance'); + assert(parent.childClicks === undefined, 'parent instance has no child property'); + + child.remove(); + parent.remove(); +}); + +it('plain class without decorators (missing metadata link) works correctly', () => { + // Test that a class without decorators in the middle of the inheritance chain + // works correctly and doesn't have Symbol.metadata polluted onto it + + class DecoratedBase extends XElement { + @property({ type: String, initial: 'base-value' }) + accessor baseProp; + + @property({ type: Number, initial: 1 }) + accessor generation; + } + + // Plain class with NO decorators - this is the "missing link" + class PlainMiddle extends DecoratedBase { + // No decorators! Just plain class extending decorated base + } + + class DecoratedChild extends PlainMiddle { + @property({ type: String, initial: 'child-value' }) + accessor childProp; + + @property({ type: Number, initial: 3 }) + accessor generation; + } + + customElements.define('test-decorated-base-link', DecoratedBase); + customElements.define('test-plain-middle-link', PlainMiddle); + customElements.define('test-decorated-child-link', DecoratedChild); + + // CRITICAL: Verify PlainMiddle doesn't have Symbol.metadata as OWN property + // (it might inherit from parent via prototype chain, but shouldn't have its own) + assert(!Object.hasOwn(PlainMiddle, Symbol.metadata), 'plain class has no own Symbol.metadata property'); + + // Verify DecoratedBase and DecoratedChild DO have Symbol.metadata (from decorators) + assert(DecoratedBase[Symbol.metadata] !== undefined, 'decorated base has Symbol.metadata'); + assert(DecoratedChild[Symbol.metadata] !== undefined, 'decorated child has Symbol.metadata'); + + // Create instances and verify inheritance still works correctly + const base = document.createElement('test-decorated-base-link'); + document.body.append(base); + + assert(base.baseProp === 'base-value', 'base has baseProp'); + assert(base.generation === 1, 'base has generation 1'); + assert(base.childProp === undefined, 'base has no child property'); + + const middle = document.createElement('test-plain-middle-link'); + document.body.append(middle); + + assert(middle.baseProp === 'base-value', 'middle inherits baseProp from base'); + assert(middle.generation === 1, 'middle inherits generation from base'); + assert(middle.childProp === undefined, 'middle has no child property'); + + const child = document.createElement('test-decorated-child-link'); + document.body.append(child); + + assert(child.baseProp === 'base-value', 'child inherits baseProp through plain middle'); + assert(child.childProp === 'child-value', 'child has childProp'); + assert(child.generation === 3, 'child overrides generation'); + + // Verify that modifications to child don't affect middle or base + child.baseProp = 'modified'; + assert(middle.baseProp === 'base-value', 'middle unchanged after child modification'); + assert(base.baseProp === 'base-value', 'base unchanged after child modification'); + + base.remove(); + middle.remove(); + child.remove(); +}); diff --git a/test-next/test-initial-properties.html b/test-next/test-initial-properties.html new file mode 100644 index 0000000..355358b --- /dev/null +++ b/test-next/test-initial-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Initial Properties

+ + + diff --git a/test-next/test-initial-properties.js b/test-next/test-initial-properties.js new file mode 100644 index 0000000..206eb85 --- /dev/null +++ b/test-next/test-initial-properties.js @@ -0,0 +1,222 @@ +let _initProto, _oneDecs, _init_one, _initProto2, _oneDecs2, _init_one2, _twoDecs, _init_two, _initProto3, _oneDecs3, _init_one3, _twoDecs2, _init_two2, _initProto4, _compoundDecs, _init_compound; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +class TestElementBasic extends XElement { + static { + [_init_one, _initProto] = _applyDecs(this, [[_oneDecs, 1, "one"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_one(this)); + get [(_oneDecs = property({ + type: String, + initial: 'one' + }), "one")]() { + return this.#A; + } + set one(v) { + this.#A = v; + } + static template(host) { + return html`
${host.one}
`; + } +} +customElements.define('test-element-basic-next', TestElementBasic); +class TestElementAnonymous extends XElement { + static { + [_init_one2, _init_two, _initProto2] = _applyDecs(this, [[_oneDecs2, 1, "one"], [_twoDecs, 1, "two"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_one2(this)); + get [(_oneDecs2 = property({ + type: String, + initial: (() => 'one')() + }), _twoDecs = property({ + type: String, + initial: () => 'two' + }), "one")]() { + return this.#A; + } + set one(v) { + this.#A = v; + } + #B = _init_two(this); + get two() { + return this.#B; + } + set two(v) { + this.#B = v; + } + static template(host) { + return html`
${host.one}, ${host.two}
`; + } +} +customElements.define('test-element-anonymous-next', TestElementAnonymous); +class TestElementStatic extends XElement { + static { + [_init_one3, _init_two2, _initProto3] = _applyDecs(this, [[_oneDecs3, 1, "one"], [_twoDecs2, 1, "two"]], [], 0, void 0, XElement).e; + } + static get [(_oneDecs3 = property({ + type: String, + initial: () => TestElementStatic.getOne() + }), _twoDecs2 = property({ + type: String, + initial: () => TestElementStatic.getTwo() + }), "one")]() { + return 'one'; + } + static getOne() { + return this.one; + } + static get two() { + return 'two'; + } + static getTwo() { + return this.two; + } + #A = (_initProto3(this), _init_one3(this)); + get one() { + return this.#A; + } + set one(v) { + this.#A = v; + } + #B = _init_two2(this); + get two() { + return this.#B; + } + set two(v) { + this.#B = v; + } + static template(host) { + return html`
${host.one}, ${host.two}
`; + } +} +customElements.define('test-element-static-next', TestElementStatic); +class Compound { + get foo() { + return 'foo'; + } +} +class TestElementCompound extends XElement { + static { + [_init_compound, _initProto4] = _applyDecs(this, [[_compoundDecs, 1, "compound"]], [], 0, void 0, XElement).e; + } + #A = (_initProto4(this), _init_compound(this)); + get [(_compoundDecs = property({ + type: Compound, + initial: () => new Compound() + }), "compound")]() { + return this.#A; + } + set compound(v) { + this.#A = v; + } + static template(host) { + return html`
${host.compound.foo}
`; + } +} +customElements.define('test-element-compound-next', TestElementCompound); +it('basic initial properties', () => { + const el = document.createElement('test-element-basic-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic initial properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic initial properties (predefined null properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); +it('basic initial properties (predefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = 'ONE'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); +it('basic initial properties (predefined attributes)', () => { + const el = document.createElement('test-element-basic-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); +it('anonymous initial properties', () => { + const el = document.createElement('test-element-anonymous-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous initial properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = undefined; + el.two = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous initial properties (predefined null properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = null; + el.two = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); +it('anonymous initial properties (predefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = 'ONE'; + el.two = 'TWO'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); +it('anonymous initial properties (predefined attributes)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.setAttribute('one', 'ONE'); + el.setAttribute('two', 'TWO'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); +it('compound initial properties are not shared accross element instances', () => { + const el1 = document.createElement('test-element-compound-next'); + const el2 = document.createElement('test-element-compound-next'); + document.body.append(el1, el2); + assert(el1.shadowRoot.textContent === 'foo'); + assert(el2.shadowRoot.textContent === 'foo'); + assert(el1.compound !== el2.compound); +}); +it('cannot set initial to a bad type', () => { + let _initProto5, _badDecs, _init_bad; + class BadTestElement extends XElement { + static { + [_init_bad, _initProto5] = _applyDecs(this, [[_badDecs, 1, "bad"]], [], 0, void 0, XElement).e; + } + #A = (_initProto5(this), _init_bad(this)); + get [(_badDecs = property({ + type: String, + initial: 0 + }), "bad")]() { + return this.#A; + } + set bad(v) { + this.#A = v; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.bad" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnRCYXNpYyIsIl9pbml0X29uZSIsIl9pbml0UHJvdG8iLCJfYXBwbHlEZWNzIiwiX29uZURlY3MiLCJlIiwiQSIsInR5cGUiLCJTdHJpbmciLCJpbml0aWFsIiwib25lIiwidiIsInRlbXBsYXRlIiwiaG9zdCIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiVGVzdEVsZW1lbnRBbm9ueW1vdXMiLCJfaW5pdF9vbmUyIiwiX2luaXRfdHdvIiwiX2luaXRQcm90bzIiLCJfb25lRGVjczIiLCJfdHdvRGVjcyIsIkIiLCJ0d28iLCJUZXN0RWxlbWVudFN0YXRpYyIsIl9pbml0X29uZTMiLCJfaW5pdF90d28yIiwiX2luaXRQcm90bzMiLCJfb25lRGVjczMiLCJfdHdvRGVjczIiLCJnZXRPbmUiLCJnZXRUd28iLCJDb21wb3VuZCIsImZvbyIsIlRlc3RFbGVtZW50Q29tcG91bmQiLCJfaW5pdF9jb21wb3VuZCIsIl9pbml0UHJvdG80IiwiX2NvbXBvdW5kRGVjcyIsImNvbXBvdW5kIiwiZWwiLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJib2R5IiwiYXBwZW5kIiwic2hhZG93Um9vdCIsInRleHRDb250ZW50IiwidW5kZWZpbmVkIiwic2V0QXR0cmlidXRlIiwiZWwxIiwiZWwyIiwiX2luaXRQcm90bzUiLCJfYmFkRGVjcyIsIl9pbml0X2JhZCIsIkJhZFRlc3RFbGVtZW50IiwiYmFkIiwicGFzc2VkIiwibWVzc2FnZSIsImNvbm5lY3RlZENhbGxiYWNrIiwiZXJyb3IiLCJleHBlY3RlZCJdLCJzb3VyY2VzIjpbInRlc3QtaW5pdGlhbC1wcm9wZXJ0aWVzLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHksIGh0bWwgfSBmcm9tICcuLi94LWVsZW1lbnQtbmV4dC5qcyc7XG5cbmNsYXNzIFRlc3RFbGVtZW50QmFzaWMgZXh0ZW5kcyBYRWxlbWVudCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogJ29uZScgfSlcbiAgYWNjZXNzb3Igb25lO1xuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3Qub25lfTwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnLCBUZXN0RWxlbWVudEJhc2ljKTtcblxuY2xhc3MgVGVzdEVsZW1lbnRBbm9ueW1vdXMgZXh0ZW5kcyBYRWxlbWVudCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogKCgpID0+ICdvbmUnKSgpIH0pXG4gIGFjY2Vzc29yIG9uZTtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBTdHJpbmcsIGluaXRpYWw6ICgpID0+ICd0d28nIH0pXG4gIGFjY2Vzc29yIHR3bztcblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm9uZX0sICR7aG9zdC50d299PC9kaXY+YDtcbiAgfVxufVxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtYW5vbnltb3VzLW5leHQnLCBUZXN0RWxlbWVudEFub255bW91cyk7XG5cbmNsYXNzIFRlc3RFbGVtZW50U3RhdGljIGV4dGVuZHMgWEVsZW1lbnQge1xuICBzdGF0aWMgZ2V0IG9uZSgpIHtcbiAgICByZXR1cm4gJ29uZSc7XG4gIH1cbiAgc3RhdGljIGdldE9uZSgpIHtcbiAgICByZXR1cm4gdGhpcy5vbmU7XG4gIH1cbiAgc3RhdGljIGdldCB0d28oKSB7XG4gICAgcmV0dXJuICd0d28nO1xuICB9XG4gIHN0YXRpYyBnZXRUd28oKSB7XG4gICAgcmV0dXJuIHRoaXMudHdvO1xuICB9XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAoKSA9PiBUZXN0RWxlbWVudFN0YXRpYy5nZXRPbmUoKSB9KVxuICBhY2Nlc3NvciBvbmU7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAoKSA9PiBUZXN0RWxlbWVudFN0YXRpYy5nZXRUd28oKSB9KVxuICBhY2Nlc3NvciB0d287XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGA8ZGl2PiR7aG9zdC5vbmV9LCAke2hvc3QudHdvfTwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LXN0YXRpYy1uZXh0JywgVGVzdEVsZW1lbnRTdGF0aWMpO1xuXG5jbGFzcyBDb21wb3VuZCB7XG4gIGdldCBmb28oKSB7XG4gICAgcmV0dXJuICdmb28nO1xuICB9XG59XG5cbmNsYXNzIFRlc3RFbGVtZW50Q29tcG91bmQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gIEBwcm9wZXJ0eSh7IHR5cGU6IENvbXBvdW5kLCBpbml0aWFsOiAoKSA9PiBuZXcgQ29tcG91bmQoKSB9KVxuICBhY2Nlc3NvciBjb21wb3VuZDtcblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0LmNvbXBvdW5kLmZvb308L2Rpdj5gO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1jb21wb3VuZC1uZXh0JywgVGVzdEVsZW1lbnRDb21wb3VuZCk7XG5cbml0KCdiYXNpYyBpbml0aWFsIHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZScpO1xufSk7XG5cbml0KCdiYXNpYyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgdW5kZWZpbmVkIHByb3BlcnRpZXMpJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1iYXNpYy1uZXh0Jyk7XG4gIGVsLm9uZSA9IHVuZGVmaW5lZDtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZScpO1xufSk7XG5cbml0KCdiYXNpYyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgbnVsbCBwcm9wZXJ0aWVzKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYmFzaWMtbmV4dCcpO1xuICBlbC5vbmUgPSBudWxsO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnb25lJyk7XG59KTtcblxuaXQoJ2Jhc2ljIGluaXRpYWwgcHJvcGVydGllcyAocHJlZGVmaW5lZCBwcm9wZXJ0aWVzKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYmFzaWMtbmV4dCcpO1xuICBlbC5vbmUgPSAnT05FJztcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ09ORScpO1xufSk7XG5cbml0KCdiYXNpYyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgYXR0cmlidXRlcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWJhc2ljLW5leHQnKTtcbiAgZWwuc2V0QXR0cmlidXRlKCdvbmUnLCAnT05FJyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdPTkUnKTtcbn0pO1xuXG5pdCgnYW5vbnltb3VzIGluaXRpYWwgcHJvcGVydGllcycsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYW5vbnltb3VzLW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZSwgdHdvJyk7XG59KTtcblxuaXQoJ2Fub255bW91cyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgdW5kZWZpbmVkIHByb3BlcnRpZXMpJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1hbm9ueW1vdXMtbmV4dCcpO1xuICBlbC5vbmUgPSB1bmRlZmluZWQ7XG4gIGVsLnR3byA9IHVuZGVmaW5lZDtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZSwgdHdvJyk7XG59KTtcblxuaXQoJ2Fub255bW91cyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgbnVsbCBwcm9wZXJ0aWVzKScsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtYW5vbnltb3VzLW5leHQnKTtcbiAgZWwub25lID0gbnVsbDtcbiAgZWwudHdvID0gbnVsbDtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ29uZSwgdHdvJyk7XG59KTtcblxuaXQoJ2Fub255bW91cyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgcHJvcGVydGllcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWFub255bW91cy1uZXh0Jyk7XG4gIGVsLm9uZSA9ICdPTkUnO1xuICBlbC50d28gPSAnVFdPJztcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ09ORSwgVFdPJyk7XG59KTtcblxuaXQoJ2Fub255bW91cyBpbml0aWFsIHByb3BlcnRpZXMgKHByZWRlZmluZWQgYXR0cmlidXRlcyknLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LWFub255bW91cy1uZXh0Jyk7XG4gIGVsLnNldEF0dHJpYnV0ZSgnb25lJywgJ09ORScpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ3R3bycsICdUV08nKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ09ORSwgVFdPJyk7XG59KTtcblxuaXQoJ2NvbXBvdW5kIGluaXRpYWwgcHJvcGVydGllcyBhcmUgbm90IHNoYXJlZCBhY2Nyb3NzIGVsZW1lbnQgaW5zdGFuY2VzJywgKCkgPT4ge1xuICBjb25zdCBlbDEgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtY29tcG91bmQtbmV4dCcpO1xuICBjb25zdCBlbDIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtY29tcG91bmQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbDEsIGVsMik7XG4gIGFzc2VydChlbDEuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ2ZvbycpO1xuICBhc3NlcnQoZWwyLnNoYWRvd1Jvb3QudGV4dENvbnRlbnQgPT09ICdmb28nKTtcbiAgYXNzZXJ0KGVsMS5jb21wb3VuZCAhPT0gZWwyLmNvbXBvdW5kKTtcbn0pO1xuXG5pdCgnY2Fubm90IHNldCBpbml0aWFsIHRvIGEgYmFkIHR5cGUnLCAoKSA9PiB7XG4gIGNsYXNzIEJhZFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZywgaW5pdGlhbDogMCB9KVxuICAgIGFjY2Vzc29yIGJhZDtcbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ2JhZC10ZXN0LWVsZW1lbnQtbmV4dC0xJywgQmFkVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBCYWRUZXN0RWxlbWVudCgpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmNvbm5lY3RlZENhbGxiYWNrKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCB2YWx1ZSBmb3IgXCJCYWRUZXN0RWxlbWVudC5wcm90b3R5cGUuYmFkXCIgKGV4cGVjdGVkIFN0cmluZywgZ290IE51bWJlcikuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxTQUFTQSxNQUFNLEVBQUVDLEVBQUUsUUFBUSwyQkFBMkI7QUFDdEQsU0FBU0MsUUFBUSxFQUFFQyxRQUFRLEVBQUVDLElBQUksUUFBUSxzQkFBc0I7QUFFL0QsTUFBTUMsZ0JBQWdCLFNBQVNILFFBQVEsQ0FBQztFQUFBO0lBQUEsQ0FBQUksU0FBQSxFQUFBQyxVQUFBLElBQUFDLFVBQUEsU0FBQUMsUUFBQSw2QkFBVFAsUUFBUSxFQUFBUSxDQUFBO0VBQUE7RUFBQSxDQUFBQyxDQUFBLElBQUFKLFVBQUEsUUFBQUQsU0FBQTtFQUFBLE1BQUFHLFFBQUEsR0FDcENOLFFBQVEsQ0FBQztJQUFFUyxJQUFJLEVBQUVDLE1BQU07SUFBRUMsT0FB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Q2hFRCxRQUFRLENBQUNFLElBQUksQ0FBQ0MsTUFBTSxDQUFDSyxHQUFHLEVBQUVDLEdBQUcsQ0FBQztFQUM5QnRELE1BQU0sQ0FBQ3FELEdBQUcsQ0FBQ0osVUFBVSxDQUFDQyxXQUFXLEtBQUssS0FBSyxDQUFDO0VBQzVDbEQsTUFBTSxDQUFDc0QsR0FBRyxDQUFDTCxVQUFVLENBQUNDLFdBQVcsS0FBSyxLQUFLLENBQUM7RUFDNUNsRCxNQUFNLENBQUNxRCxHQUFHLENBQUNWLFFBQVEsS0FBS1csR0FBRyxDQUFDWCxRQUFRLENBQUM7QUFDdkMsQ0FBQyxDQUFDO0FBRUYxQyxFQUFFLENBQUMsa0NBQWtDLEVBQUUsTUFBTTtFQUFBLElBQUFzRCxXQUFBLEVBQUFDLFFBQUEsRUFBQUMsU0FBQTtFQUMzQyxNQUFNQyxjQUFjLFNBQVN4RCxRQUFRLENBQUM7SUFBQTtNQUFBLENBQUF1RCxTQUFBLEVBQUFGLFdBQUEsSUFBQS9DLFVBQUEsU0FBQWdELFFBQUEsNkJBQVR0RCxRQUFRLEVBQUFRLENBQUE7SUFBQTtJQUFBLENBQUFDLENBQUEsSUFBQTRDLFdBQUEsUUFBQUUsU0FBQTtJQUFBLE1BQUFELFFBQUEsR0FDbENyRCxRQUFRLENBQUM7TUFBRVMsSUFBSSxFQUFFQyxNQUFNO01BQUVDLE9BQU8sRUFBRTtJQUFFLENBQUMsQ0FBQztNQUFBLGFBQUFILENBQUE7SUFBQTtJQUFBLElBQzlCZ0QsR0FBR0EsQ0FBQTNDLENBQUE7TUFBQSxNQUFBTCxDQUFBLEdBQUFLLENBQUE7SUFBQTtFQUNkO0VBQ0FHLGNBQWMsQ0FBQ0MsTUFBTSxDQUFDLHlCQUF5QixFQUFFc0MsY0FBYyxDQUFDO0VBQ2hFLE1BQU1kLEVBQUUsR0FBRyxJQUFJYyxjQUFjLENBQUMsQ0FBQztFQUMvQixJQUFJRSxNQUFNLEdBQUcsS0FBSztFQUNsQixJQUFJQyxPQUFPLEdBQUcscUJBQXFCO0VBQ25DLElBQUk7SUFDRmpCLEVBQUUsQ0FBQ2tCLGlCQUFpQixDQUFDLENBQUM7RUFDeEIsQ0FBQyxDQUFDLE9BQU9DLEtBQUssRUFBRTtJQUNkLE1BQU1DLFFBQVEsR0FBRyxvRkFBb0Y7SUFDckdILE9BQU8sR0FBR0UsS0FBSyxDQUFDRixPQUFPO0lBQ3ZCRCxNQUFNLEdBQUdHLEtBQUssQ0FBQ0YsT0FBTyxLQUFLRyxRQUFRO0VBQ3JDO0VBQ0FoRSxNQUFNLENBQUM0RCxNQUFNLEVBQUVDLE9BQU8sQ0FBQztBQUN6QixDQUFDLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/test-next/test-initial-properties.src.js b/test-next/test-initial-properties.src.js new file mode 100644 index 0000000..93c83e9 --- /dev/null +++ b/test-next/test-initial-properties.src.js @@ -0,0 +1,167 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +class TestElementBasic extends XElement { + @property({ type: String, initial: 'one' }) + accessor one; + + static template(host) { + return html`
${host.one}
`; + } +} +customElements.define('test-element-basic-next', TestElementBasic); + +class TestElementAnonymous extends XElement { + @property({ type: String, initial: (() => 'one')() }) + accessor one; + + @property({ type: String, initial: () => 'two' }) + accessor two; + + static template(host) { + return html`
${host.one}, ${host.two}
`; + } +} +customElements.define('test-element-anonymous-next', TestElementAnonymous); + +class TestElementStatic extends XElement { + static get one() { + return 'one'; + } + static getOne() { + return this.one; + } + static get two() { + return 'two'; + } + static getTwo() { + return this.two; + } + + @property({ type: String, initial: () => TestElementStatic.getOne() }) + accessor one; + + @property({ type: String, initial: () => TestElementStatic.getTwo() }) + accessor two; + + static template(host) { + return html`
${host.one}, ${host.two}
`; + } +} +customElements.define('test-element-static-next', TestElementStatic); + +class Compound { + get foo() { + return 'foo'; + } +} + +class TestElementCompound extends XElement { + @property({ type: Compound, initial: () => new Compound() }) + accessor compound; + + static template(host) { + return html`
${host.compound.foo}
`; + } +} +customElements.define('test-element-compound-next', TestElementCompound); + +it('basic initial properties', () => { + const el = document.createElement('test-element-basic-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic initial properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic initial properties (predefined null properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one'); +}); + +it('basic initial properties (predefined properties)', () => { + const el = document.createElement('test-element-basic-next'); + el.one = 'ONE'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); + +it('basic initial properties (predefined attributes)', () => { + const el = document.createElement('test-element-basic-next'); + el.setAttribute('one', 'ONE'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE'); +}); + +it('anonymous initial properties', () => { + const el = document.createElement('test-element-anonymous-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous initial properties (predefined undefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = undefined; + el.two = undefined; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous initial properties (predefined null properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = null; + el.two = null; + document.body.append(el); + assert(el.shadowRoot.textContent === 'one, two'); +}); + +it('anonymous initial properties (predefined properties)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.one = 'ONE'; + el.two = 'TWO'; + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); + +it('anonymous initial properties (predefined attributes)', () => { + const el = document.createElement('test-element-anonymous-next'); + el.setAttribute('one', 'ONE'); + el.setAttribute('two', 'TWO'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'ONE, TWO'); +}); + +it('compound initial properties are not shared accross element instances', () => { + const el1 = document.createElement('test-element-compound-next'); + const el2 = document.createElement('test-element-compound-next'); + document.body.append(el1, el2); + assert(el1.shadowRoot.textContent === 'foo'); + assert(el2.shadowRoot.textContent === 'foo'); + assert(el1.compound !== el2.compound); +}); + +it('cannot set initial to a bad type', () => { + class BadTestElement extends XElement { + @property({ type: String, initial: 0 }) + accessor bad; + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "BadTestElement.prototype.bad" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-initialization-errors.html b/test-next/test-initialization-errors.html new file mode 100644 index 0000000..85fbe65 --- /dev/null +++ b/test-next/test-initialization-errors.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Initialization Errors

+ + + diff --git a/test-next/test-initialization-errors.js b/test-next/test-initialization-errors.js new file mode 100644 index 0000000..74fab1c --- /dev/null +++ b/test-next/test-initialization-errors.js @@ -0,0 +1,181 @@ +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +it('errors are thrown in connectedCallback for initializing values with bad types', () => { + let _initProto, _stringDecs, _init_string; + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static { + [_init_string, _initProto] = _applyDecs(this, [[_stringDecs, 1, "string"]], [], 0, void 0, XElement).e; + } + #A = (_initProto(this), _init_string(this)); + get [(_stringDecs = property({ + type: String + }), "string")]() { + return this.#A; + } + set string(v) { + this.#A = v; + } + } + customElements.define('test-element-init-type', TestElement); + const el = new TestElement(); + el.string = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.string" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Users should use accessor-only decorators (no setter) or private fields +// to achieve read-only behavior. Skip this test as readOnly is not supported. +it.skip('errors are thrown in connectedCallback for initializing read-only properties', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static get properties() { + return { + readOnlyProperty: { + type: String, + readOnly: true + } + }; + } + } + customElements.define('test-element-1', TestElement); + const el = new TestElement(); + el.readOnlyProperty = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "TestElement.prototype.readOnlyProperty" is read-only.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('errors are thrown in connectedCallback for initializing computed properties', () => { + let _initProto2, _computedDecs, _init_computed; + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static { + [_init_computed, _initProto2] = _applyDecs(this, [[_computedDecs, 1, "computed"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_computed(this)); + get [(_computedDecs = property({ + type: String, + input: [], + compute: () => {} + }), "computed")]() { + return this.#A; + } + set computed(v) { + this.#A = v; + } + } + customElements.define('test-element-init-computed', TestElement); + const el = new TestElement(); + el.computed = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "TestElement.prototype.computed" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// Depending on the browser — the underlying error is surfaced differently. +// We just match our custom suffix to be agnostic. +it('errors are thrown in connectedCallback when template result fails to render', () => { + let _initProto3, _stringsDecs, _init_strings; + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static { + [_init_strings, _initProto3] = _applyDecs(this, [[_stringsDecs, 1, "strings"]], [], 0, void 0, XElement).e; + } + #A = (_initProto3(this), _init_strings(this)); + get [(_stringsDecs = property({ + default: () => ['one', 'two', 'three'] + }), "strings")]() { + return this.#A; + } + set strings(v) { + this.#A = v; + } + static template(host) { + // In this case, the array will fail if items are not template results. + return html`
${host.strings}
`; + } + } + customElements.define('test-element-render-fail', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid template for "TestElement" / at path "test-element-render-fail".'; + message = error.message; + passed = error.message.endsWith(expected); + } + assert(passed, message); +}); + +// Depending on the browser — the underlying error is surfaced differently. +// We just match our custom suffix to be agnostic. +it('errors are thrown in connectedCallback when template result fails to render (with ids, classes, and attributes)', () => { + let _initProto4, _stringsDecs2, _init_strings2; + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static { + [_init_strings2, _initProto4] = _applyDecs(this, [[_stringsDecs2, 1, "strings"]], [], 0, void 0, XElement).e; + } + #A = (_initProto4(this), _init_strings2(this)); + get [(_stringsDecs2 = property({ + default: () => ['one', 'two', 'three'] + }), "strings")]() { + return this.#A; + } + set strings(v) { + this.#A = v; + } + static template(host) { + // In this case, the array will fail if items are not template results. + return html`
${host.strings}
`; + } + } + customElements.define('test-element-render-fail-details', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + el.id = 'testing'; + el.classList.add('foo'); + el.classList.add('bar'); + el.setAttribute('boolean', ''); + el.setAttribute('variation', 'primary'); + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid template for "TestElement" / at path "test-element-render-fail-details[id="testing"][class="foo bar"][boolean][variation="primary"]".'; + message = error.message; + passed = error.message.endsWith(expected); + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiX2luaXRQcm90byIsIl9zdHJpbmdEZWNzIiwiX2luaXRfc3RyaW5nIiwiVGVzdEVsZW1lbnQiLCJfYXBwbHlEZWNzIiwiZSIsIkEiLCJ0eXBlIiwiU3RyaW5nIiwic3RyaW5nIiwidiIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiZWwiLCJwYXNzZWQiLCJtZXNzYWdlIiwiY29ubmVjdGVkQ2FsbGJhY2siLCJlcnJvciIsImV4cGVjdGVkIiwic2tpcCIsInByb3BlcnRpZXMiLCJyZWFkT25seVByb3BlcnR5IiwicmVhZE9ubHkiLCJfaW5pdFByb3RvMiIsIl9jb21wdXRlZERlY3MiLCJfaW5pdF9jb21wdXRlZCIsImlucHV0IiwiY29tcHV0ZSIsImNvbXB1dGVkIiwiX2luaXRQcm90bzMiLCJfc3RyaW5nc0RlY3MiLCJfaW5pdF9zdHJpbmdzIiwiZGVmYXVsdCIsInN0cmluZ3MiLCJ0ZW1wbGF0ZSIsImhvc3QiLCJlbmRzV2l0aCIsIl9pbml0UHJvdG80IiwiX3N0cmluZ3NEZWNzMiIsIl9pbml0X3N0cmluZ3MyIiwiaWQiLCJjbGFzc0xpc3QiLCJhZGQiLCJzZXRBdHRyaWJ1dGUiXSwic291cmNlcyI6WyJ0ZXN0LWluaXRpYWxpemF0aW9uLWVycm9ycy5zcmMuanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYXNzZXJ0LCBpdCB9IGZyb20gJ0BuZXRmbGl4L3gtdGVzdC94LXRlc3QuanMnO1xuaW1wb3J0IHsgWEVsZW1lbnQsIHByb3BlcnR5LCBodG1sIH0gZnJvbSAnLi4veC1lbGVtZW50LW5leHQuanMnO1xuXG5pdCgnZXJyb3JzIGFyZSB0aHJvd24gaW4gY29ubmVjdGVkQ2FsbGJhY2sgZm9yIGluaXRpYWxpemluZyB2YWx1ZXMgd2l0aCBiYWQgdHlwZXMnLCAoKSA9PiB7XG4gIC8vIFdlIGNhbm5vdCB0cnktY2F0Y2ggYXBwZW5kLCBzbyB3ZSBmYWtlIHRoZSBjb25uZWN0ZWRDYWxsYmFjay5cbiAgY2xhc3MgVGVzdEVsZW1lbnQgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nIH0pXG4gICAgYWNjZXNzb3Igc3RyaW5nO1xuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1lbGVtZW50LWluaXQtdHlwZScsIFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVzdEVsZW1lbnQoKTtcbiAgZWwuc3RyaW5nID0gNTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQgdmFsdWUgZm9yIFwiVGVzdEVsZW1lbnQucHJvdG90eXBlLnN0cmluZ1wiIChleHBlY3RlZCBTdHJpbmcsIGdvdCBOdW1iZXIpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogVGhlIHJlYWRPbmx5IGNvbmZpZ3VyYXRpb24gZG9lc24ndCBleGlzdCBpbiB0aGUgZGVjb3JhdG9yIEFQSS5cbi8vICBVc2VycyBzaG91bGQgdXNlIGFjY2Vzc29yLW9ubHkgZGVjb3JhdG9ycyAobm8gc2V0dGVyKSBvciBwcml2YXRlIGZpZWxkc1xuLy8gIHRvIGFjaGlldmUgcmVhZC1vbmx5IGJlaGF2aW9yLiBTa2lwIHRoaXMgdGVzdCBhcyByZWFkT25seSBpcyBub3Qgc3VwcG9ydGVkLlxuaXQuc2tpcCgnZXJyb3JzIGFyZSB0aHJvd24gaW4gY29ubmVjdGVkQ2FsbGJhY2sgZm9yIGluaXRpYWxpemluZyByZWFkLW9ubHkgcHJvcGVydGllcycsICgpID0+IHtcbiAgLy8gV2UgY2Fubm90IHRyeS1jYXRjaCBhcHBlbmQsIHNvIHdlIGZha2UgdGhlIGNvbm5lY3RlZENhbGxiYWNrLlxuICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBzdGF0aWMgZ2V0IHByb3BlcnRpZXMoKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICByZWFkT25seVByb3BlcnR5OiB7XG4gICAgICAgICAgdHlwZTogU3RyaW5nLFxuICAgICAgICAgIHJlYWRPbmx5OiB0cnVlLFxuICAgICAgICB9LFxuICAgICAgfTtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtMScsIFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVzdEVsZW1lbnQoKTtcbiAgZWwucmVhZE9ubHlQcm9wZXJ0eSA9IDU7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5yZWFkT25seVByb3BlcnR5XCIgaXMgcmVhZC1vbmx5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ2Vycm9ycyBhcmUgdGhyb3duIGluIGNvbm5lY3RlZENhbGxiYWNrIGZvciBpbml0aWFsaXppbmcgY29tcHV0ZWQgcHJvcGVydGllcycsICgpID0+IHtcbiAgLy8gV2UgY2Fubm90IHRyeS1jYXRjaCBhcHBlbmQsIHNvIHdlIGZha2UgdGhlIGNvbm5lY3RlZENhbGxiYWNrLlxuICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoe1xuICAgICAgdHlwZTogU3RyaW5nLFxuICAgICAgaW5wdXQ6IFtdLFxuICAgICAgY29tcHV0ZTogKCkgPT4ge30sXG4gICAgfSlcbiAgICBhY2Nlc3NvciBjb21wdXRlZDtcbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1pbml0LWNvbXB1dGVkJywgVGVzdEVsZW1lbnQpO1xuICBjb25zdCBlbCA9IG5ldyBUZXN0RWxlbWVudCgpO1xuICBlbC5jb21wdXRlZCA9IDU7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3RvdHlwZS5jb21wdXRlZFwiIGlzIGNvbXB1dGVkIChjb21wdXRlZCBwcm9wZXJ0aWVzIGFyZSByZWFkLW9ubHkpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gRGVwZW5kaW5nIG9uIHRoZSBicm93c2VyIOKAlCB0aGUgdW5kZXJseWluZyBlcnJvciBpcyBzdXJmYWNlZCBkaWZmZXJlbnRseS5cbi8vIFdlIGp1c3QgbWF0Y2ggb3VyIGN1c3RvbSBzdWZmaXggdG8gYmUgYWdub3N0aWMuXG5pdCgnZXJyb3JzIGFyZSB0aHJvd24gaW4gY29ubmVjdGVkQ2FsbGJhY2sgd2hlbiB0ZW1wbGF0ZSByZXN1bHQgZmFpbHMgdG8gcmVuZGVyJywgKCkgPT4ge1xuICAvLyBXZSBjYW5ub3QgdHJ5LWNhdGNoIGFwcGVuZCwgc28gd2UgZmFrZSB0aGUgY29ubmVjdGVkQ2FsbGJhY2suXG4gIGNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IGRlZmF1bHQ6ICgpID0+IFsnb25lJywgJ3R3bycsICd0aHJlZSddIH0pXG4gICAgYWNjZXNzb3Igc3RyaW5ncztcblxuICAgIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgICAvLyBJbiB0aGlzIGNhc2UsIHRoZSBhcnJheSB3aWxsIGZhaWwgaWYgaXRlbXMgYXJlIG5vdCB0ZW1wbGF0ZSByZXN1bHRzLlxuICAgICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3Quc3RyaW5nc308L2Rpdj5gO1xuICAgIH1cbiAgfVxuICBjdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1yZW5kZXItZmFpbCcsIFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5jb25uZWN0ZWRDYWxsYmFjaygpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdGVtcGxhdGUgZm9yIFwiVGVzdEVsZW1lbnRcIiAvIDx0ZXN0LWVsZW1lbnQtcmVuZGVyLWZhaWw+IGF0IHBhdGggXCJ0ZXN0LWVsZW1lbnQtcmVuZGVyLWZhaWxcIi4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UuZW5kc1dpdGgoZXhwZWN0ZWQpO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIERlcGVuZGluZyBvbiB0aGUgYnJvd3NlciDigJQgdGhlIHVuZGVybHlpbmcgZXJyb3IgaXMgc3VyZmFjZWQgZGlmZmVyZW50bHkuXG4vLyBXZSBqdXN0IG1hdGNoIG91ciBjdXN0b20gc3VmZml4IHRvIGJlIGFnbm9zdGljLlxuaXQoJ2Vycm9ycyBhcmUgdGhyb3duIGluIGNvbm5lY3RlZENhbGxiYWNrIHdoZW4gdGVtcGxhdGUgcmVzdWx0IGZhaWxzIHRvIHJlbmRlciAod2l0aCBpZHMsIGNsYXNzZXMsIGFuZCBhdHRyaWJ1dGVzKScsICgpID0+IHtcbiAgLy8gV2UgY2Fubm90IHRyeS1jYXRjaCBhcHBlbmQsIHNvIHdlIGZha2UgdGhlIGNvbm5lY3RlZENhbGxiYWNrLlxuICBjbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoeyBkZWZhdWx0OiAoKSA9PiBbJ29uZScsICd0d28nLCAndGhyZWUnXSB9KVxuICAgIGFjY2Vzc29yIHN0cmluZ3M7XG5cbiAgICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgICAgLy8gSW4gdGhpcyBjYXNlLCB0aGUgYXJyYXkgd2lsbCBmYWlsIGlmIGl0ZW1zIGFyZSBub3QgdGVtcGxhdGUgcmVzdWx0cy5cbiAgICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0LnN0cmluZ3N9PC9kaXY+YDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWVsZW1lbnQtcmVuZGVyLWZhaWwtZGV0YWlscycsIFRlc3RFbGVtZW50KTtcbiAgY29uc3QgZWwgPSBuZXcgVGVzdEVsZW1lbnQoKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgZWwuaWQgPSAndGVzdGluZyc7XG4gIGVsLmNsYXNzTGlzdC5hZGQoJ2ZvbycpO1xuICBlbC5jbGFzc0xpc3QuYWRkKCdiYXInKTtcbiAgZWwuc2V0QXR0cmlidXRlKCdib29sZWFuJywgJycpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ3ZhcmlhdGlvbicsICdwcmltYXJ5Jyk7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdJbnZhbGlkIHRlbXBsYXRlIGZvciBcIlRlc3RFbGVtZW50XCIgLyA8dGVzdC1lbGVtZW50LXJlbmRlci1mYWlsLWRldGFpbHM+IGF0IHBhdGggXCJ0ZXN0LWVsZW1lbnQtcmVuZGVyLWZhaWwtZGV0YWlsc1tpZD1cInRlc3RpbmdcIl1bY2xhc3M9XCJmb28gYmFyXCJdW2Jvb2xlYW5dW3ZhcmlhdGlvbj1cInByaW1hcnlcIl1cIi4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UuZW5kc1dpdGgoZXhwZWN0ZWQpO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsU0FBU0EsTUFBTSxFQUFFQyxFQUFFLFFBQVEsMkJBQTJCO0FBQ3RELFNBQVNDLFFBQVEsRUFBRUMsUUFBUSxFQUFFQyxJQUFJLFFBQVEsc0JBQXNCO0FBRS9ESCxFQUFFLENBQUMsK0VBQStFLEVBQUUsTUFBTTtFQUFBLElBQUFJLFVBQUEsRUFBQUMsV0FBQSxFQUFBQyxZQUFBO0VBQ3hGO0VBQ0EsTUFBTUMsV0FBVyxTQUFTTixRQUFRLENBQUM7SUFBQTtNQUFBLENBQUFLLFlBQUEsRUFBQUYsVUFBQSxJQUFBSSxVQUFBLFNBQUFILFdBQUEsZ0NBQVRKLFFBQVEsRUFBQVEsQ0FBQTtJQUFBO0lBQUEsQ0FBQUMsQ0FBQSxJQUFBTixVQUFBLFFBQUFFLFlBQUE7SUFBQSxNQUFBRCxXQUFBLEdBQy9CSCxRQUFRLENBQUM7TUFBRVMsSUFBSSxFQUFFQztJQUFPLENBQUMsQ0FBQztNQUFBLGFBQUFGLENBQUE7SUFBQTtJQUFBLElBQ2xCRyxNQUFNQSxDQUFBQyxDQUFBO01BQUEsTUFBQUosQ0FBQSxHQUFBSSxDQUFBO0lBQUE7RUFDakI7RUFDQUMsY0FBYyxDQUFDQyxNQUFNLENBQUMsd0JBQXdCLEVBQUVULFdBQVcsQ0FBQztFQUM1RCxNQUFNVSxFQUFFLEdBQUcsSUFBSVYsV0FBVyxDQUFDLENBQUM7RUFDNUJVLEVBQUUs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 \ No newline at end of file diff --git a/test-next/test-initialization-errors.src.js b/test-next/test-initialization-errors.src.js new file mode 100644 index 0000000..ea5febd --- /dev/null +++ b/test-next/test-initialization-errors.src.js @@ -0,0 +1,137 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +it('errors are thrown in connectedCallback for initializing values with bad types', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + @property({ type: String }) + accessor string; + } + customElements.define('test-element-init-type', TestElement); + const el = new TestElement(); + el.string = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Unexpected value for "TestElement.prototype.string" (expected String, got Number).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: The readOnly configuration doesn't exist in the decorator API. +// Users should use accessor-only decorators (no setter) or private fields +// to achieve read-only behavior. Skip this test as readOnly is not supported. +it.skip('errors are thrown in connectedCallback for initializing read-only properties', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + static get properties() { + return { + readOnlyProperty: { + type: String, + readOnly: true, + }, + }; + } + } + customElements.define('test-element-1', TestElement); + const el = new TestElement(); + el.readOnlyProperty = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "TestElement.prototype.readOnlyProperty" is read-only.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('errors are thrown in connectedCallback for initializing computed properties', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + @property({ + type: String, + input: [], + compute: () => {}, + }) + accessor computed; + } + customElements.define('test-element-init-computed', TestElement); + const el = new TestElement(); + el.computed = 5; + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Property "TestElement.prototype.computed" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// Depending on the browser — the underlying error is surfaced differently. +// We just match our custom suffix to be agnostic. +it('errors are thrown in connectedCallback when template result fails to render', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + @property({ default: () => ['one', 'two', 'three'] }) + accessor strings; + + static template(host) { + // In this case, the array will fail if items are not template results. + return html`
${host.strings}
`; + } + } + customElements.define('test-element-render-fail', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid template for "TestElement" / at path "test-element-render-fail".'; + message = error.message; + passed = error.message.endsWith(expected); + } + assert(passed, message); +}); + +// Depending on the browser — the underlying error is surfaced differently. +// We just match our custom suffix to be agnostic. +it('errors are thrown in connectedCallback when template result fails to render (with ids, classes, and attributes)', () => { + // We cannot try-catch append, so we fake the connectedCallback. + class TestElement extends XElement { + @property({ default: () => ['one', 'two', 'three'] }) + accessor strings; + + static template(host) { + // In this case, the array will fail if items are not template results. + return html`
${host.strings}
`; + } + } + customElements.define('test-element-render-fail-details', TestElement); + const el = new TestElement(); + let passed = false; + let message = 'no error was thrown'; + el.id = 'testing'; + el.classList.add('foo'); + el.classList.add('bar'); + el.setAttribute('boolean', ''); + el.setAttribute('variation', 'primary'); + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Invalid template for "TestElement" / at path "test-element-render-fail-details[id="testing"][class="foo bar"][boolean][variation="primary"]".'; + message = error.message; + passed = error.message.endsWith(expected); + } + assert(passed, message); +}); diff --git a/test-next/test-internal-properties.html b/test-next/test-internal-properties.html new file mode 100644 index 0000000..011a7b3 --- /dev/null +++ b/test-next/test-internal-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Internal Properties

+ + + diff --git a/test-next/test-internal-properties.js b/test-next/test-internal-properties.js new file mode 100644 index 0000000..ba28856 --- /dev/null +++ b/test-next/test-internal-properties.js @@ -0,0 +1,318 @@ +let _initProto, _internalPropertyDecs, _init_internalProperty, _get_internalProperty, _set_internalProperty, _internalComputedPropertyDecs, _init_internalComputedProperty, _get_internalComputedProperty, _set_internalComputedProperty, _childrenDecs, _init_children, _get_children, _set_children; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +// TODO: #346: We likely should rename this to test-private-properties and +// rewrite to target idiomatic private fields in the future. + +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +class TestElement extends XElement { + static { + [_init_internalProperty, _get_internalProperty, _set_internalProperty, _init_internalComputedProperty, _get_internalComputedProperty, _set_internalComputedProperty, _init_children, _get_children, _set_children, _initProto] = _applyDecs(this, [[_internalPropertyDecs, 1, "internalProperty", o => o.#A, (o, v) => o.#A = v], [_internalComputedPropertyDecs, 1, "internalComputedProperty", o => o.#B, (o, v) => o.#B = v], [_childrenDecs, 1, "children", o => o.#C, (o, v) => o.#C = v]], [], 0, _ => #internalProperty in _, XElement).e; + } + #A = (_initProto(this), _init_internalProperty(this)); + set #internalProperty(v) { + _set_internalProperty(this, v); + } + get #internalProperty() { + return _get_internalProperty(this); + } + #B = _init_internalComputedProperty(this); // This exists on the public interface, we want to ensure that there's no + // issue using this same name on the internal interface. + set #internalComputedProperty(v) { + _set_internalComputedProperty(this, v); + } + get #internalComputedProperty() { + return _get_internalComputedProperty(this); + } + #C = _init_children(this); + set #children(v) { + _set_children(this, v); + } + get #children() { + return _get_children(this); + } + static [(_internalPropertyDecs = property({ + type: String, + initial: 'Ferus' + }), _internalComputedPropertyDecs = property({ + type: String, + input: ['#internalProperty'], + compute: internalProperty => internalProperty + }), _childrenDecs = property({ + // Use a type that's in conflict with the public interface. + type: Boolean + }), "template")](host) { + return html`
${host.#internalProperty}
`; + } +} +customElements.define('test-element-next', TestElement); +it('initialization', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'Ferus', 'initialized as expected'); +}); + +// TODO: #346: We can likely delete. It used to test the "in" operator on +// el.internal proxy, but decorator API uses native private fields (#). +it.skip('can use "has" api or "in" operator.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert('internalProperty' in el.internal, 'The "has" trap does not work.'); +}); + +// TODO: #346: We can likely delete. It used to test Reflect.ownKeys on el.internal +// proxy, but decorator API uses native private fields which don't appear in ownKeys. +it.skip('can use "ownKeys" api.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const ownKeys = Reflect.ownKeys(el.internal); + assert(ownKeys.length === 3 && ownKeys[0] === 'internalProperty' && ownKeys[1] === 'internalComputedProperty' && ownKeys[2] === 'children', 'The "ownKeys" trap does not work.'); +}); +it('cannot be read on host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internalProperty === undefined); +}); +it('cannot be written to on host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internalProperty === undefined); + el.internalProperty = 'ignored'; + assert(el.internalProperty === 'ignored'); +}); + +// TODO: #346: We can likely delete. It used to test reading via el.internal proxy, +// but decorator API uses native private fields which can only be accessed within the class. +it.skip('can be read from "internal"', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internal.internalProperty === 'Ferus'); +}); + +// TODO: #346: We can likely delete. It used to test writing via el.internal proxy, +// but decorator API uses native private fields which can only be modified within the class. +it.skip('can be written to from "internal"', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.internal.internalProperty = 'Dromedary'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'Dromedary', 'written to as expected'); +}); + +// TODO: #346: We can likely delete. It used to test computed property read-only +// enforcement via el.internal proxy, but decorator API uses native private fields. +it.skip('cannot be written to from "internal" if computed', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.internalComputedProperty = `don't do it`; + } catch (error) { + const expected = 'Property "TestElement.properties.internalComputedProperty" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy constraints in +// template, but decorator API receives the host directly. Private fields can't be accessed +// from static methods anyway (syntax error). +it.skip('cannot set to known properties', () => { + let _initProto2, _internalPropertyDecs2, _init_internalProperty2, _get_internalProperty2, _set_internalProperty2; + class BadTestElement extends XElement { + static { + [_init_internalProperty2, _get_internalProperty2, _set_internalProperty2, _initProto2] = _applyDecs(this, [[_internalPropertyDecs2, 1, "internalProperty", o => o.#A, (o, v) => o.#A = v]], [], 0, _ => #internalProperty in _, XElement).e; + } + #A = (_initProto2(this), _init_internalProperty2(this)); + set #internalProperty(v) { + _set_internalProperty2(this, v); + } + get #internalProperty() { + return _get_internalProperty2(this); + } + static [(_internalPropertyDecs2 = property({ + type: String + }), "template")](host) { + // This would be a syntax error - can't access private field from static method + // host.#internalProperty = 'Dromedary'; + return html`
${host.#internalProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Cannot set "BadTestElement.properties.internalProperty" via "properties".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy errors for +// unknown property access, but decorator API uses native private fields. +it.skip('cannot get unknown properties', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.doesNotExist; + } catch (error) { + const expected = 'Property "TestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy errors for +// unknown property writes, but decorator API uses native private fields. +it.skip('cannot get unknown properties', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.doesNotExist = 'nope'; + } catch (error) { + const expected = 'Property "TestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "defineProperty" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.defineProperty(el.internal, 'foo', {}); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// This is a funny one, you can set to undefined, but we strictly don't let you +// "delete" since it has a different meaning and you strictly cannot delete our +// accessors. +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "delete" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.deleteProperty(el.internal, 'internalProperty'); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "getOwnPropertyDescriptor" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.getOwnPropertyDescriptor(el.internal, 'internalProperty'); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "getPrototypeOf" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.getPrototypeOf(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "isExtensible" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.isExtensible(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "preventExtensions" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.preventExtensions(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "setPrototypeOf" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.setPrototypeOf(el.internal, Array); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnQiLCJfaW5pdF9pbnRlcm5hbFByb3BlcnR5IiwiX2dldF9pbnRlcm5hbFByb3BlcnR5IiwiX3NldF9pbnRlcm5hbFByb3BlcnR5IiwiX2luaXRfaW50ZXJuYWxDb21wdXRlZFByb3BlcnR5IiwiX2dldF9pbnRlcm5hbENvbXB1dGVkUHJvcGVydHkiLCJfc2V0X2ludGVybmFsQ29tcHV0ZWRQcm9wZXJ0eSIsIl9pbml0X2NoaWxkcmVuIiwiX2dldF9jaGlsZHJlbiIsIl9zZXRfY2hpbGRyZW4iLCJfaW5pdFByb3RvIiwiX2FwcGx5RGVjcyIsIl9pbnRlcm5hbFByb3BlcnR5RGVjcyIsIm8iLCJBIiwidiIsIl9pbnRlcm5hbENvbXB1dGVkUHJvcGVydHlEZWNzIiwiQiIsIl9jaGlsZHJlbkRlY3MiLCJDIiwiXyIsImludGVybmFsUHJvcGVydHkiLCJlIiwiI2ludGVybmFsUHJvcGVydHkiLCJpbnRlcm5hbENvbXB1dGVkUHJvcGVydHkiLCIjaW50ZXJuYWxDb21wdXRlZFByb3BlcnR5IiwiY2hpbGRyZW4iLCIjY2hpbGRyZW4iLCJ0eXBlIiwiU3RyaW5nIiwiaW5pdGlhbCIsImlucHV0IiwiY29tcHV0ZSIsIkJvb2xlYW4iLCJob3N0IiwiY3VzdG9tRWxlbWVudHMiLCJkZWZpbmUiLCJlbCIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudCIsImJvZHkiLCJhcHBlbmQiLCJzaGFkb3dSb290IiwidGV4dENvbnRlbnQiLCJza2lwIiwiaW50ZXJuYWwiLCJvd25LZXlzIiwiUmVmbGVjdCIsImxlbmd0aCIsInVuZGVmaW5lZCIsIlByb21pc2UiLCJyZXNvbHZlIiwicGFzc2VkIiwibWVzc2FnZSIsImVycm9yIiwiZXhwZWN0ZWQiLCJfaW5pdFByb3RvMiIsIl9pbnRlcm5hbFByb3BlcnR5RGVjczIiLCJfaW5pdF9pbnRlcm5hbFByb3BlcnR5MiIsIl9nZXRfaW50ZXJuYWxQcm9wZXJ0eTIiLCJfc2V0X2ludGVybmFsUHJvcGVydHkyIiwiQmFkVGVzdEVsZW1lbnQiLCJjb25uZWN0ZWRDYWxsYmFjayIsImRvZXNOb3RFeGlzdCIsImRlZmluZVByb3BlcnR5IiwiZGVsZXRlUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJnZXRQcm90b3R5cGVPZiIsImlzRXh0ZW5zaWJsZSIsInByZXZlbnRFeHRlbnNpb25zIiwic2V0UHJvdG90eXBlT2YiLCJBcnJheSJdLCJzb3VyY2VzIjpbInRlc3QtaW50ZXJuYWwtcHJvcGVydGllcy5zcmMuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gVE9ETzogIzM0NjogV2UgbGlrZWx5IHNob3VsZCByZW5hbWUgdGhpcyB0byB0ZXN0LXByaXZhdGUtcHJvcGVydGllcyBhbmRcbi8vICByZXdyaXRlIHRvIHRhcmdldCBpZGlvbWF0aWMgcHJpdmF0ZSBmaWVsZHMgaW4gdGhlIGZ1dHVyZS5cblxuaW1wb3J0IHsgYXNzZXJ0LCBpdCB9IGZyb20gJ0BuZXRmbGl4L3gtdGVzdC94LXRlc3QuanMnO1xuaW1wb3J0IHsgWEVsZW1lbnQsIHByb3BlcnR5LCBodG1sIH0gZnJvbSAnLi4veC1lbGVtZW50LW5leHQuanMnO1xuXG5jbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogU3RyaW5nLCBpbml0aWFsOiAnRmVydXMnIH0pXG4gIGFjY2Vzc29yICNpbnRlcm5hbFByb3BlcnR5O1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogU3RyaW5nLFxuICAgIGlucHV0OiBbJyNpbnRlcm5hbFByb3BlcnR5J10sXG4gICAgY29tcHV0ZTogaW50ZXJuYWxQcm9wZXJ0eSA9PiBpbnRlcm5hbFByb3BlcnR5LFxuICB9KVxuICBhY2Nlc3NvciAjaW50ZXJuYWxDb21wdXRlZFByb3BlcnR5O1xuXG4gIC8vIFRoaXMgZXhpc3RzIG9uIHRoZSBwdWJsaWMgaW50ZXJmYWNlLCB3ZSB3YW50IHRvIGVuc3VyZSB0aGF0IHRoZXJlJ3Mgbm9cbiAgLy8gIGlzc3VlIHVzaW5nIHRoaXMgc2FtZSBuYW1lIG9uIHRoZSBpbnRlcm5hbCBpbnRlcmZhY2UuXG4gIEBwcm9wZXJ0eSh7XG4gICAgLy8gVXNlIGEgdHlwZSB0aGF0J3MgaW4gY29uZmxpY3Qgd2l0aCB0aGUgcHVibGljIGludGVyZmFjZS5cbiAgICB0eXBlOiBCb29sZWFuLFxuICB9KVxuICBhY2Nlc3NvciAjY2hpbGRyZW47XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGA8ZGl2PiR7aG9zdC4jaW50ZXJuYWxQcm9wZXJ0eX08L2Rpdj5gO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1uZXh0JywgVGVzdEVsZW1lbnQpO1xuXG5pdCgnaW5pdGlhbGl6YXRpb24nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuc2hhZG93Um9vdC50ZXh0Q29udGVudCA9PT0gJ0ZlcnVzJywgJ2luaXRpYWxpemVkIGFzIGV4cGVjdGVkJyk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCB0aGUgXCJpblwiIG9wZXJhdG9yIG9uXG4vLyAgZWwuaW50ZXJuYWwgcHJveHksIGJ1dCBkZWNvcmF0b3IgQVBJIHVzZXMgbmF0aXZlIHByaXZhdGUgZmllbGRzICgjKS5cbml0LnNraXAoJ2NhbiB1c2UgXCJoYXNcIiBhcGkgb3IgXCJpblwiIG9wZXJhdG9yLicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydCgnaW50ZXJuYWxQcm9wZXJ0eScgaW4gZWwuaW50ZXJuYWwsICdUaGUgXCJoYXNcIiB0cmFwIGRvZXMgbm90IHdvcmsuJyk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBSZWZsZWN0Lm93bktleXMgb24gZWwuaW50ZXJuYWxcbi8vICBwcm94eSwgYnV0IGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMgd2hpY2ggZG9uJ3QgYXBwZWFyIGluIG93bktleXMuXG5pdC5za2lwKCdjYW4gdXNlIFwib3duS2V5c1wiIGFwaS4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBjb25zdCBvd25LZXlzID0gUmVmbGVjdC5vd25LZXlzKGVsLmludGVybmFsKTtcbiAgYXNzZXJ0KFxuICAgIG93bktleXMubGVuZ3RoID09PSAzICYmXG4gICAgb3duS2V5c1swXSA9PT0gJ2ludGVybmFsUHJvcGVydHknICYmXG4gICAgb3duS2V5c1sxXSA9PT0gJ2ludGVybmFsQ29tcHV0ZWRQcm9wZXJ0eScgJiZcbiAgICBvd25LZXlzWzJdID09PSAnY2hpbGRyZW4nLFxuICAgICdUaGUgXCJvd25LZXlzXCIgdHJhcCBkb2VzIG5vdCB3b3JrLidcbiAgKTtcbn0pO1xuXG5pdCgnY2Fubm90IGJlIHJlYWQgb24gaG9zdCcsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5pbnRlcm5hbFByb3BlcnR5ID09PSB1bmRlZmluZWQpO1xufSk7XG5cbml0KCdjYW5ub3QgYmUgd3JpdHRlbiB0byBvbiBob3N0JywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLmludGVybmFsUHJvcGVydHkgPT09IHVuZGVmaW5lZCk7XG4gIGVsLmludGVybmFsUHJvcGVydHkgPSAnaWdub3JlZCc7XG4gIGFzc2VydChlbC5pbnRlcm5hbFByb3BlcnR5ID09PSAnaWdub3JlZCcpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgcmVhZGluZyB2aWEgZWwuaW50ZXJuYWwgcHJveHksXG4vLyAgYnV0IGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMgd2hpY2ggY2FuIG9ubHkgYmUgYWNjZXNzZWQgd2l0aGluIHRoZSBjbGFzcy5cbml0LnNraXAoJ2NhbiBiZSByZWFkIGZyb20gXCJpbnRlcm5hbFwiJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgYXNzZXJ0KGVsLmludGVybmFsLmludGVybmFsUHJvcGVydHkgPT09ICdGZXJ1cycpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3Qgd3JpdGluZyB2aWEgZWwuaW50ZXJuYWwgcHJveHksXG4vLyAgYnV0IGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMgd2hpY2ggY2FuIG9ubHkgYmUgbW9kaWZpZWQgd2l0aGluIHRoZSBjbGFzcy5cbml0LnNraXAoJ2NhbiBiZSB3cml0dGVuIHRvIGZyb20gXCJpbnRlcm5hbFwiJywgYXN5bmMgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgZWwuaW50ZXJuYWwuaW50ZXJuYWxQcm9wZXJ0eSA9ICdEcm9tZWRhcnknO1xuXG4gIC8vIFdlIG11c3QgYXdhaXQgYSBtaWNyb3Rhc2sgZm9yIHRoZSB1cGRhdGUgdG8gdGFrZSBwbGFjZS5cbiAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCk7XG4gIGFzc2VydChlbC5zaGFkb3dSb290LnRleHRDb250ZW50ID09PSAnRHJvbWVkYXJ5JywgJ3dyaXR0ZW4gdG8gYXMgZXhwZWN0ZWQnKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IGNvbXB1dGVkIHByb3BlcnR5IHJlYWQtb25seVxuLy8gIGVuZm9yY2VtZW50IHZpYSBlbC5pbnRlcm5hbCBwcm94eSwgYnV0IGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMuXG5pdC5za2lwKCdjYW5ub3QgYmUgd3JpdHRlbiB0byBmcm9tIFwiaW50ZXJuYWxcIiBpZiBjb21wdXRlZCcsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuaW50ZXJuYWwuaW50ZXJuYWxDb21wdXRlZFByb3BlcnR5ID0gYGRvbid0IGRvIGl0YDtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3BlcnRpZXMuaW50ZXJuYWxDb21wdXRlZFByb3BlcnR5XCIgaXMgY29tcHV0ZWQgKGNvbXB1dGVkIHByb3BlcnRpZXMgYXJlIHJlYWQtb25seSkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IHByb3BlcnRpZXMgcHJveHkgY29uc3RyYWludHMgaW5cbi8vICB0ZW1wbGF0ZSwgYnV0IGRlY29yYXRvciBBUEkgcmVjZWl2ZXMgdGhlIGhvc3QgZGlyZWN0bHkuIFByaXZhdGUgZmllbGRzIGNhbid0IGJlIGFjY2Vzc2VkXG4vLyAgZnJvbSBzdGF0aWMgbWV0aG9kcyBhbnl3YXkgKHN5bnRheCBlcnJvcikuXG5pdC5za2lwKCdjYW5ub3Qgc2V0IHRvIGtub3duIHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNsYXNzIEJhZFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICAgIEBwcm9wZXJ0eSh7IHR5cGU6IFN0cmluZyB9KVxuICAgIGFjY2Vzc29yICNpbnRlcm5hbFByb3BlcnR5O1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIC8vIFRoaXMgd291bGQgYmUgYSBzeW50YXggZXJyb3IgLSBjYW4ndCBhY2Nlc3MgcHJpdmF0ZSBmaWVsZCBmcm9tIHN0YXRpYyBtZXRob2RcbiAgICAgIC8vIGhvc3QuI2ludGVybmFsUHJvcGVydHkgPSAnRHJvbWVkYXJ5JztcbiAgICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0LiNpbnRlcm5hbFByb3BlcnR5fTwvZGl2PmA7XG4gICAgfVxuICB9XG4gIGN1c3RvbUVsZW1lbnRzLmRlZmluZSgnYmFkLXRlc3QtZWxlbWVudC1uZXh0LTEnLCBCYWRUZXN0RWxlbWVudCk7XG4gIGNvbnN0IGVsID0gbmV3IEJhZFRlc3RFbGVtZW50KCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdDYW5ub3Qgc2V0IFwiQmFkVGVzdEVsZW1lbnQucHJvcGVydGllcy5pbnRlcm5hbFByb3BlcnR5XCIgdmlhIFwicHJvcGVydGllc1wiLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBlbC5pbnRlcm5hbCBwcm94eSBlcnJvcnMgZm9yXG4vLyAgdW5rbm93biBwcm9wZXJ0eSBhY2Nlc3MsIGJ1dCBkZWNvcmF0b3IgQVBJIHVzZXMgbmF0aXZlIHByaXZhdGUgZmllbGRzLlxuaXQuc2tpcCgnY2Fubm90IGdldCB1bmtub3duIHByb3BlcnRpZXMnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmludGVybmFsLmRvZXNOb3RFeGlzdDtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdQcm9wZXJ0eSBcIlRlc3RFbGVtZW50LnByb3BlcnRpZXMuZG9lc05vdEV4aXN0XCIgZG9lcyBub3QgZXhpc3QuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IGVsLmludGVybmFsIHByb3h5IGVycm9ycyBmb3Jcbi8vICB1bmtub3duIHByb3BlcnR5IHdyaXRlcywgYnV0IGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMuXG5pdC5za2lwKCdjYW5ub3QgZ2V0IHVua25vd24gcHJvcGVydGllcycsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwuaW50ZXJuYWwuZG9lc05vdEV4aXN0ID0gJ25vcGUnO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1Byb3BlcnR5IFwiVGVzdEVsZW1lbnQucHJvcGVydGllcy5kb2VzTm90RXhpc3RcIiBkb2VzIG5vdCBleGlzdC4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgZWwuaW50ZXJuYWwgcHJveHkgdHJhcCwgYnV0XG4vLyAgZGVjb3JhdG9yIEFQSSB1c2VzIG5hdGl2ZSBwcml2YXRlIGZpZWxkcyB3aXRob3V0IHByb3h5LlxuaXQuc2tpcCgnY2Fubm90IFwiZGVmaW5lUHJvcGVydHlcIiBvbiBpbnRlcm5hbC4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIFJlZmxlY3QuZGVmaW5lUHJvcGVydHkoZWwuaW50ZXJuYWwsICdmb28nLCB7fSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgaW50ZXJuYWwgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUaGlzIGlzIGEgZnVubnkgb25lLCB5b3UgY2FuIHNldCB0byB1bmRlZmluZWQsIGJ1dCB3ZSBzdHJpY3RseSBkb24ndCBsZXQgeW91XG4vLyBcImRlbGV0ZVwiIHNpbmNlIGl0IGhhcyBhIGRpZmZlcmVudCBtZWFuaW5nIGFuZCB5b3Ugc3RyaWN0bHkgY2Fubm90IGRlbGV0ZSBvdXJcbi8vIGFjY2Vzc29ycy5cbi8vIFRPRE86ICMzNDY6IFdlIGNhbiBsaWtlbHkgZGVsZXRlLiBJdCB1c2VkIHRvIHRlc3QgZWwuaW50ZXJuYWwgcHJveHkgdHJhcCwgYnV0XG4vLyAgZGVjb3JhdG9yIEFQSSB1c2VzIG5hdGl2ZSBwcml2YXRlIGZpZWxkcyB3aXRob3V0IHByb3h5LlxuaXQuc2tpcCgnY2Fubm90IFwiZGVsZXRlXCIgb24gaW50ZXJuYWwuJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBSZWZsZWN0LmRlbGV0ZVByb3BlcnR5KGVsLmludGVybmFsLCAnaW50ZXJuYWxQcm9wZXJ0eScpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdXNlIG9mIGludGVybmFsIHByb3h5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBlbC5pbnRlcm5hbCBwcm94eSB0cmFwLCBidXRcbi8vICBkZWNvcmF0b3IgQVBJIHVzZXMgbmF0aXZlIHByaXZhdGUgZmllbGRzIHdpdGhvdXQgcHJveHkuXG5pdC5za2lwKCdjYW5ub3QgXCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JcIiBvbiBpbnRlcm5hbC4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIFJlZmxlY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGVsLmludGVybmFsLCAnaW50ZXJuYWxQcm9wZXJ0eScpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdXNlIG9mIGludGVybmFsIHByb3h5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBlbC5pbnRlcm5hbCBwcm94eSB0cmFwLCBidXRcbi8vICBkZWNvcmF0b3IgQVBJIHVzZXMgbmF0aXZlIHByaXZhdGUgZmllbGRzIHdpdGhvdXQgcHJveHkuXG5pdC5za2lwKCdjYW5ub3QgXCJnZXRQcm90b3R5cGVPZlwiIG9uIGludGVybmFsLicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgUmVmbGVjdC5nZXRQcm90b3R5cGVPZihlbC5pbnRlcm5hbCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgaW50ZXJuYWwgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IGVsLmludGVybmFsIHByb3h5IHRyYXAsIGJ1dFxuLy8gIGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMgd2l0aG91dCBwcm94eS5cbml0LnNraXAoJ2Nhbm5vdCBcImlzRXh0ZW5zaWJsZVwiIG9uIGludGVybmFsLicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgUmVmbGVjdC5pc0V4dGVuc2libGUoZWwuaW50ZXJuYWwpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ0ludmFsaWQgdXNlIG9mIGludGVybmFsIHByb3h5Lic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuLy8gVE9ETzogIzM0NjogV2UgY2FuIGxpa2VseSBkZWxldGUuIEl0IHVzZWQgdG8gdGVzdCBlbC5pbnRlcm5hbCBwcm94eSB0cmFwLCBidXRcbi8vICBkZWNvcmF0b3IgQVBJIHVzZXMgbmF0aXZlIHByaXZhdGUgZmllbGRzIHdpdGhvdXQgcHJveHkuXG5pdC5za2lwKCdjYW5ub3QgXCJwcmV2ZW50RXh0ZW5zaW9uc1wiIG9uIGludGVybmFsLicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgUmVmbGVjdC5wcmV2ZW50RXh0ZW5zaW9ucyhlbC5pbnRlcm5hbCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgaW50ZXJuYWwgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUT0RPOiAjMzQ2OiBXZSBjYW4gbGlrZWx5IGRlbGV0ZS4gSXQgdXNlZCB0byB0ZXN0IGVsLmludGVybmFsIHByb3h5IHRyYXAsIGJ1dFxuLy8gIGRlY29yYXRvciBBUEkgdXNlcyBuYXRpdmUgcHJpdmF0ZSBmaWVsZHMgd2l0aG91dCBwcm94eS5cbml0LnNraXAoJ2Nhbm5vdCBcInNldFByb3RvdHlwZU9mXCIgb24gaW50ZXJuYWwuJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBSZWZsZWN0LnNldFByb3RvdHlwZU9mKGVsLmludGVybmFsLCBBcnJheSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnSW52YWxpZCB1c2Ugb2YgaW50ZXJuYWwgcHJveHkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQTtBQUNBOztBQUVBLFNBQVNBLE1BQU0sRUFBRUMsRUFBRSxRQUFRLDJCQUEyQjtBQUN0RCxTQUFTQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsSUFBSSxRQUFRLHNCQUFzQjtBQUUvRCxNQUFNQyxXQUFXLFNBQVNILFFBQVEsQ0FBQztFQUFBO0lBQUEsQ0FBQUksc0JBQUEsRUFBQUMscUJBQUEsRUFBQUMscUJBQUEsRUFBQUMsOEJBQUEsRUFBQUMsNkJBQUEsRUFBQUMsNkJBQUEsRUFBQUMsY0FBQSxFQUFBQyxhQUFBLEVBQUFDLGFBQUEsRUFBQUMsVUFBQSxJQUFBQyxVQUFBLFNBQUFDLHFCQUFBLHlCQUFBQyxDQUFBLElBQUFBLENBQUEsRUFBQUMsQ0FBQSxHQUFBRCxDQUFBLEVBQUFFLENBQUEsS0FBQUYsQ0FBQSxFQUFBQyxDQUFBLEdBQUFDLENBQUEsSUFBQUMsNkJBQUEsaUNBQUFILENBQUEsSUFBQUEsQ0FBQSxFQUFBSSxDQUFBLEdBQUFK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 \ No newline at end of file diff --git a/test-next/test-internal-properties.src.js b/test-next/test-internal-properties.src.js new file mode 100644 index 0000000..2175d8b --- /dev/null +++ b/test-next/test-internal-properties.src.js @@ -0,0 +1,294 @@ +// TODO: #346: We likely should rename this to test-private-properties and +// rewrite to target idiomatic private fields in the future. + +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +class TestElement extends XElement { + @property({ type: String, initial: 'Ferus' }) + accessor #internalProperty; + + @property({ + type: String, + input: ['#internalProperty'], + compute: internalProperty => internalProperty, + }) + accessor #internalComputedProperty; + + // This exists on the public interface, we want to ensure that there's no + // issue using this same name on the internal interface. + @property({ + // Use a type that's in conflict with the public interface. + type: Boolean, + }) + accessor #children; + + static template(host) { + return html`
${host.#internalProperty}
`; + } +} +customElements.define('test-element-next', TestElement); + +it('initialization', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.shadowRoot.textContent === 'Ferus', 'initialized as expected'); +}); + +// TODO: #346: We can likely delete. It used to test the "in" operator on +// el.internal proxy, but decorator API uses native private fields (#). +it.skip('can use "has" api or "in" operator.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert('internalProperty' in el.internal, 'The "has" trap does not work.'); +}); + +// TODO: #346: We can likely delete. It used to test Reflect.ownKeys on el.internal +// proxy, but decorator API uses native private fields which don't appear in ownKeys. +it.skip('can use "ownKeys" api.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const ownKeys = Reflect.ownKeys(el.internal); + assert( + ownKeys.length === 3 && + ownKeys[0] === 'internalProperty' && + ownKeys[1] === 'internalComputedProperty' && + ownKeys[2] === 'children', + 'The "ownKeys" trap does not work.' + ); +}); + +it('cannot be read on host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internalProperty === undefined); +}); + +it('cannot be written to on host', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internalProperty === undefined); + el.internalProperty = 'ignored'; + assert(el.internalProperty === 'ignored'); +}); + +// TODO: #346: We can likely delete. It used to test reading via el.internal proxy, +// but decorator API uses native private fields which can only be accessed within the class. +it.skip('can be read from "internal"', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.internal.internalProperty === 'Ferus'); +}); + +// TODO: #346: We can likely delete. It used to test writing via el.internal proxy, +// but decorator API uses native private fields which can only be modified within the class. +it.skip('can be written to from "internal"', async () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + el.internal.internalProperty = 'Dromedary'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(el.shadowRoot.textContent === 'Dromedary', 'written to as expected'); +}); + +// TODO: #346: We can likely delete. It used to test computed property read-only +// enforcement via el.internal proxy, but decorator API uses native private fields. +it.skip('cannot be written to from "internal" if computed', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.internalComputedProperty = `don't do it`; + } catch (error) { + const expected = 'Property "TestElement.properties.internalComputedProperty" is computed (computed properties are read-only).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test properties proxy constraints in +// template, but decorator API receives the host directly. Private fields can't be accessed +// from static methods anyway (syntax error). +it.skip('cannot set to known properties', () => { + class BadTestElement extends XElement { + @property({ type: String }) + accessor #internalProperty; + + static template(host) { + // This would be a syntax error - can't access private field from static method + // host.#internalProperty = 'Dromedary'; + return html`
${host.#internalProperty}
`; + } + } + customElements.define('bad-test-element-next-1', BadTestElement); + const el = new BadTestElement(); + let passed = false; + let message = 'no error was thrown'; + try { + el.connectedCallback(); + } catch (error) { + const expected = 'Cannot set "BadTestElement.properties.internalProperty" via "properties".'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy errors for +// unknown property access, but decorator API uses native private fields. +it.skip('cannot get unknown properties', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.doesNotExist; + } catch (error) { + const expected = 'Property "TestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy errors for +// unknown property writes, but decorator API uses native private fields. +it.skip('cannot get unknown properties', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.internal.doesNotExist = 'nope'; + } catch (error) { + const expected = 'Property "TestElement.properties.doesNotExist" does not exist.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "defineProperty" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.defineProperty(el.internal, 'foo', {}); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// This is a funny one, you can set to undefined, but we strictly don't let you +// "delete" since it has a different meaning and you strictly cannot delete our +// accessors. +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "delete" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.deleteProperty(el.internal, 'internalProperty'); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "getOwnPropertyDescriptor" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.getOwnPropertyDescriptor(el.internal, 'internalProperty'); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "getPrototypeOf" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.getPrototypeOf(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "isExtensible" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.isExtensible(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "preventExtensions" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.preventExtensions(el.internal); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// TODO: #346: We can likely delete. It used to test el.internal proxy trap, but +// decorator API uses native private fields without proxy. +it.skip('cannot "setPrototypeOf" on internal.', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + Reflect.setPrototypeOf(el.internal, Array); + } catch (error) { + const expected = 'Invalid use of internal proxy.'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); diff --git a/test-next/test-listeners.html b/test-next/test-listeners.html new file mode 100644 index 0000000..04a9880 --- /dev/null +++ b/test-next/test-listeners.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Listeners

+ + + diff --git a/test-next/test-listeners.js b/test-next/test-listeners.js new file mode 100644 index 0000000..0ca16bd --- /dev/null +++ b/test-next/test-listeners.js @@ -0,0 +1,542 @@ +let _initProto, _initStatic, _clicksDecs, _init_clicks, _connectionsDecs, _init_connections, _countDecs, _init_count, _customEventCountDecs, _init_customEventCount, _customEventOnceCountDecs, _init_customEventOnceCount, _onClickDecs, _onConnectedDecs, _initProto2, _initStatic2, _count1Decs, _init_count2, _count2Decs, _init_count3, _onTestEvent1Decs, _onTestEvent2Decs, _initProto3, _initStatic3, _onceCountDecs, _init_onceCount, _normalCountDecs, _init_normalCount, _onOnceDecs, _onNormalDecs, _initProto4, _initStatic4, _orderDecs, _init_order, _onCaptureDecs, _onBubbleDecs, _initProto5, _initStatic5, _normalCountDecs2, _init_normalCount2, _passiveCountDecs, _init_passiveCount, _onceCountDecs2, _init_onceCount2, _onNormalDecs2, _onPassiveDecs, _onOnceDecs2; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener, html } from '../x-element-next.js'; +class TestElementChild extends HTMLElement { + connectedCallback() { + const eventType = 'connected'; + const eventData = { + bubbles: true, + composed: true + }; + this.dispatchEvent(new CustomEvent(eventType, eventData)); + } +} +customElements.define('test-element-child', TestElementChild); +class TestElement extends XElement { + static { + [_init_clicks, _init_connections, _init_count, _init_customEventCount, _init_customEventOnceCount, _initProto, _initStatic] = _applyDecs(this, [[_onClickDecs, 10, "onClick"], [_onConnectedDecs, 10, "onConnected"], [_clicksDecs, 1, "clicks"], [_connectionsDecs, 1, "connections"], [_countDecs, 1, "count"], [_customEventCountDecs, 1, "customEventCount"], [_customEventOnceCountDecs, 1, "customEventOnceCount"]], [], 0, void 0, XElement).e; + _initStatic(this); + } + #A = (_initProto(this), _init_clicks(this)); + get [(_clicksDecs = property({ + type: Number, + default: 0 + }), _connectionsDecs = property({ + type: Number, + default: 0 + }), _countDecs = property({ + type: Number, + default: 0 + }), _customEventCountDecs = property({ + type: Number, + default: 0 + }), _customEventOnceCountDecs = property({ + type: Number, + default: () => 0 + }), _onClickDecs = listener('click'), _onConnectedDecs = listener('connected'), "clicks")]() { + return this.#A; + } + set clicks(v) { + this.#A = v; + } + #B = _init_connections(this); + get connections() { + return this.#B; + } + set connections(v) { + this.#B = v; + } + #C = _init_count(this); + get count() { + return this.#C; + } + set count(v) { + this.#C = v; + } + #D = _init_customEventCount(this); + get customEventCount() { + return this.#D; + } + set customEventCount(v) { + this.#D = v; + } + #E = _init_customEventOnceCount(this); + get customEventOnceCount() { + return this.#E; + } + set customEventOnceCount(v) { + this.#E = v; + } + static onClick(host, evt) { + host.clicks++; + if (evt.target.id === 'increment') { + host.count++; + } else if (evt.target.id === 'decrement') { + host.count--; + } + } + static onConnected(host) { + host.connections++; + } + static onCustomEventOnce(host) { + if (this === TestElement && host.constructor === TestElement) { + host.customEventOnceCount++; + } + } + static onCustomEvent(host) { + if (this === TestElement && host.constructor === TestElement) { + host.customEventCount++; + } + } + static template(host) { + return html` + + + clicks: ${host.clicks} count ${host.count} +
+ + `; + } + connectedCallback() { + super.connectedCallback(); + this.listen(this.shadowRoot, 'custom-event', this.constructor.onCustomEventOnce, { + once: true + }); + this.listen(this.shadowRoot, 'custom-event', this.constructor.onCustomEvent); + } + disconnectedCallback() { + super.disconnectedCallback(); + this.unlisten(this.shadowRoot, 'custom-event', this.constructor.onCustomEventOnce, { + once: true + }); + this.unlisten(this.shadowRoot, 'custom-event', this.constructor.onCustomEvent); + } +} +customElements.define('test-element-next', TestElement); +it('test lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.clicks === 0 && el.count === 0, 'initialized as expected'); + el.click(); + assert(el.clicks === 0 && el.count === 0, 'listens on shadowRoot, not host'); + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 1 && el.count === 1, 'listens to events'); + el.shadowRoot.getElementById('decrement').click(); + assert(el.clicks === 2 && el.count === 0, 'works for delegated handling'); + document.body.removeChild(el); + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 2 && el.count === 0, 'removes listeners on disconnect'); + document.body.append(el); + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 3 && el.count === 1, 'adds back listeners on reconnect'); +}); +it('test connectedCallback lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const eventEmitter = el.shadowRoot.getElementById('custom-event-emitter'); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { + bubbles: true + })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 1); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { + bubbles: true + })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 2); + document.body.removeChild(el); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { + bubbles: true + })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 2); +}); +it('test manual lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = 0; + const onManualCheck = () => count++; + el.listen(el.shadowRoot, 'manual-check', onManualCheck); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + assert(count === 2, 'listener was added'); + el.unlisten(el.shadowRoot, 'manual-check', onManualCheck); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + assert(count === 2, 'listener was removed'); +}); +it('test synchronous event handling', () => { + // This is subtle, but it tests that if child elements emit events + // synchronously in their first render, delegated event listening from the + // parent will still work. + let count = 0; + const documentListener = () => count++; + document.addEventListener('connected', documentListener); + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(count === 1); + assert(el.connections === count); +}); +it('throws for bad element on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(); + } catch (error) { + const expected = 'Unexpected element passed to listen (expected EventTarget, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad type on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el); + } catch (error) { + const expected = 'Unexpected type passed to listen (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad callback on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el.shadowRoot, 'test'); + } catch (error) { + const expected = 'Unexpected callback passed to listen (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad options on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el.shadowRoot, 'test', () => {}, true); + } catch (error) { + const expected = 'Unexpected options passed to listen (expected Object, got Boolean).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad element on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(); + } catch (error) { + const expected = 'Unexpected element passed to unlisten (expected EventTarget, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad type on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el); + } catch (error) { + const expected = 'Unexpected type passed to unlisten (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad callback on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el.shadowRoot, 'test'); + } catch (error) { + const expected = 'Unexpected callback passed to unlisten (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); +it('throws for bad options on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el.shadowRoot, 'test', () => {}, true); + } catch (error) { + const expected = 'Unexpected options passed to unlisten (expected Object, got Boolean).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// Test multiple listeners for same event type +class MultiListenerElement extends XElement { + static { + [_init_count2, _init_count3, _initProto2, _initStatic2] = _applyDecs(this, [[_onTestEvent1Decs, 10, "onTestEvent1"], [_onTestEvent2Decs, 10, "onTestEvent2"], [_count1Decs, 1, "count1"], [_count2Decs, 1, "count2"]], [], 0, void 0, XElement).e; + _initStatic2(this); + } + #A = (_initProto2(this), _init_count2(this)); + get [(_count1Decs = property({ + type: Number, + initial: 0 + }), _count2Decs = property({ + type: Number, + initial: 0 + }), _onTestEvent1Decs = listener('test-event'), _onTestEvent2Decs = listener('test-event'), "count1")]() { + return this.#A; + } + set count1(v) { + this.#A = v; + } + #B = _init_count3(this); + get count2() { + return this.#B; + } + set count2(v) { + this.#B = v; + } + static onTestEvent1(host) { + host.count1++; + } + static onTestEvent2(host) { + host.count2++; + } + static template(host) { + return html`
${host.count1}-${host.count2}
`; + } +} +customElements.define('test-multi-listener', MultiListenerElement); +it('multiple listeners for same event type', () => { + const el = document.createElement('test-multi-listener'); + document.body.append(el); + assert(el.count1 === 0 && el.count2 === 0, 'counters start at 0'); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners fire'); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.count1 === 2 && el.count2 === 2, 'both listeners fire on repeat'); + el.remove(); +}); + +// Test listener with once option +class OnceListenerElement extends XElement { + static { + [_init_onceCount, _init_normalCount, _initProto3, _initStatic3] = _applyDecs(this, [[_onOnceDecs, 10, "onOnce"], [_onNormalDecs, 10, "onNormal"], [_onceCountDecs, 1, "onceCount"], [_normalCountDecs, 1, "normalCount"]], [], 0, void 0, XElement).e; + _initStatic3(this); + } + #A = (_initProto3(this), _init_onceCount(this)); + get [(_onceCountDecs = property({ + type: Number, + initial: 0 + }), _normalCountDecs = property({ + type: Number, + initial: 0 + }), _onOnceDecs = listener('test-event', { + once: true + }), _onNormalDecs = listener('test-event'), "onceCount")]() { + return this.#A; + } + set onceCount(v) { + this.#A = v; + } + #B = _init_normalCount(this); + get normalCount() { + return this.#B; + } + set normalCount(v) { + this.#B = v; + } + static onOnce(host) { + host.onceCount++; + } + static onNormal(host) { + host.normalCount++; + } + static template(host) { + return html`
${host.onceCount}-${host.normalCount}
`; + } +} +customElements.define('test-once-listener', OnceListenerElement); +it('listener with once option fires once only', () => { + const el = document.createElement('test-once-listener'); + document.body.append(el); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.onceCount === 1 && el.normalCount === 1, 'both fire on first event'); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.onceCount === 1 && el.normalCount === 2, 'once listener stops, normal continues'); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.onceCount === 1 && el.normalCount === 3, 'once listener still stopped'); + el.remove(); +}); + +// Test listener with capture option +class CaptureListenerElement extends XElement { + static { + [_init_order, _initProto4, _initStatic4] = _applyDecs(this, [[_onCaptureDecs, 10, "onCapture"], [_onBubbleDecs, 10, "onBubble"], [_orderDecs, 1, "order"]], [], 0, void 0, XElement).e; + _initStatic4(this); + } + #A = (_initProto4(this), _init_order(this)); + get [(_orderDecs = property({ + type: Array, + initial: () => [] + }), _onCaptureDecs = listener('test-event', { + capture: true + }), _onBubbleDecs = listener('test-event'), "order")]() { + return this.#A; + } + set order(v) { + this.#A = v; + } + static onCapture(host) { + host.order = [...host.order, 'capture']; + } + static onBubble(host) { + host.order = [...host.order, 'bubble']; + } + static template() { + return html`
`; + } +} +customElements.define('test-capture-listener', CaptureListenerElement); +it('listener with capture option fires in capture phase', () => { + const el = document.createElement('test-capture-listener'); + document.body.append(el); + const child = el.shadowRoot.getElementById('child'); + child.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.order.length === 2, 'both listeners fired'); + assert(el.order[0] === 'capture', 'capture listener fired first'); + assert(el.order[1] === 'bubble', 'bubble listener fired second'); + el.remove(); +}); + +// Test multiple listeners with different options +class MixedOptionsElement extends XElement { + static { + [_init_normalCount2, _init_passiveCount, _init_onceCount2, _initProto5, _initStatic5] = _applyDecs(this, [[_onNormalDecs2, 10, "onNormal"], [_onPassiveDecs, 10, "onPassive"], [_onOnceDecs2, 10, "onOnce"], [_normalCountDecs2, 1, "normalCount"], [_passiveCountDecs, 1, "passiveCount"], [_onceCountDecs2, 1, "onceCount"]], [], 0, void 0, XElement).e; + _initStatic5(this); + } + #A = (_initProto5(this), _init_normalCount2(this)); + get [(_normalCountDecs2 = property({ + type: Number, + initial: 0 + }), _passiveCountDecs = property({ + type: Number, + initial: 0 + }), _onceCountDecs2 = property({ + type: Number, + initial: 0 + }), _onNormalDecs2 = listener('test-event'), _onPassiveDecs = listener('test-event', { + passive: true + }), _onOnceDecs2 = listener('test-event', { + once: true + }), "normalCount")]() { + return this.#A; + } + set normalCount(v) { + this.#A = v; + } + #B = _init_passiveCount(this); + get passiveCount() { + return this.#B; + } + set passiveCount(v) { + this.#B = v; + } + #C = _init_onceCount2(this); + get onceCount() { + return this.#C; + } + set onceCount(v) { + this.#C = v; + } + static onNormal(host) { + host.normalCount++; + } + static onPassive(host) { + host.passiveCount++; + } + static onOnce(host) { + host.onceCount++; + } + static template(host) { + return html`
${host.normalCount}-${host.passiveCount}-${host.onceCount}
`; + } +} +customElements.define('test-mixed-options', MixedOptionsElement); +it('multiple listeners with different options', () => { + const el = document.createElement('test-mixed-options'); + document.body.append(el); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.normalCount === 1, 'normal listener fires'); + assert(el.passiveCount === 1, 'passive listener fires'); + assert(el.onceCount === 1, 'once listener fires'); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.normalCount === 2, 'normal listener fires again'); + assert(el.passiveCount === 2, 'passive listener fires again'); + assert(el.onceCount === 1, 'once listener does not fire again'); + el.remove(); +}); + +// Test disconnection removes all multiple listeners +it('disconnection removes all multiple listeners', () => { + const el = document.createElement('test-multi-listener'); + document.body.append(el); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners work when connected'); + el.remove(); + el.count1 = 0; + el.count2 = 0; + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.count1 === 0 && el.count2 === 0, 'neither listener fires when disconnected'); + document.body.append(el); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { + bubbles: true + })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners work again when reconnected'); + el.remove(); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJsaXN0ZW5lciIsImh0bWwiLCJUZXN0RWxlbWVudENoaWxkIiwiSFRNTEVsZW1lbnQiLCJjb25uZWN0ZWRDYWxsYmFjayIsImV2ZW50VHlwZSIsImV2ZW50RGF0YSIsImJ1YmJsZXMiLCJjb21wb3NlZCIsImRpc3BhdGNoRXZlbnQiLCJDdXN0b21FdmVudCIsImN1c3RvbUVsZW1lbnRzIiwiZGVmaW5lIiwiVGVzdEVsZW1lbnQiLCJfaW5pdF9jbGlja3MiLCJfaW5pdF9jb25uZWN0aW9ucyIsIl9pbml0X2NvdW50IiwiX2luaXRfY3VzdG9tRXZlbnRDb3VudCIsIl9pbml0X2N1c3RvbUV2ZW50T25jZUNvdW50IiwiX2luaXRQcm90byIsIl9pbml0U3RhdGljIiwiX2FwcGx5RGVjcyIsIl9vbkNsaWNrRGVjcyIsIl9vbkNvbm5lY3RlZERlY3MiLCJfY2xpY2tzRGVjcyIsIl9jb25uZWN0aW9uc0RlY3MiLCJfY291bnREZWNzIiwiX2N1c3RvbUV2ZW50Q291bnREZWNzIiwiX2N1c3RvbUV2ZW50T25jZUNvdW50RGVjcyIsImUiLCJBIiwidHlwZSIsIk51bWJlciIsImRlZmF1bHQiLCJjbGlja3MiLCJ2IiwiQiIsImNvbm5lY3Rpb25zIiwiQyIsImNvdW50IiwiRCIsImN1c3RvbUV2ZW50Q291bnQiLCJFIiwiY3VzdG9tRXZlbnRPbmNlQ291bnQiLCJvbkNsaWNrIiwiaG9zdCIsImV2dCIsInRhcmdldCIsImlkIiwib25Db25uZWN0ZWQiLCJvbkN1c3RvbUV2ZW50T25jZSIsImNvbnN0cnVjdG9yIiwib25DdXN0b21FdmVudCIsInRlbXBsYXRlIiwibGlzdGVuIiwic2hhZG93Um9vdCIsIm9uY2UiLCJkaXNjb25uZWN0ZWRDYWxsYmFjayIsInVubGlzdGVuIiwiZWwiLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJib2R5IiwiYXBwZW5kIiwiY2xpY2siLCJnZXRFbGVtZW50QnlJZCIsInJlbW92ZUNoaWxkIiwiZXZlbnRFbWl0dGVyIiwib25NYW51YWxDaGVjayIsImRvY3VtZW50TGlzdGVuZXIiLCJhZGRFdmVudExpc3RlbmVyIiwicGFzc2VkIiwibWVzc2FnZSIsImVycm9yIiwiZXhwZWN0ZWQiLCJNdWx0aUxpc3RlbmVyRWxlbWVudCIsIl9pbml0X2NvdW50MiIsIl9pbml0X2NvdW50MyIsIl9pbml0UHJvdG8yIiwiX2luaXRTdGF0aWMyIiwiX29uVGVzdEV2ZW50MURlY3MiLCJfb25UZXN0RXZlbnQyRGVjcyIsIl9jb3VudDFEZWNzIiwiX2NvdW50MkRlY3MiLCJpbml0aWFsIiwiY291bnQxIiwiY291bnQyIiwib25UZXN0RXZlbnQxIiwib25UZXN0RXZlbnQyIiwicmVtb3ZlIiwiT25jZUxpc3RlbmVyRWxlbWVudCIsIl9pbml0X29uY2VDb3VudCIsIl9pbml0X25vcm1hbENvdW50IiwiX2luaXRQcm90bzMiLCJfaW5pdFN0YXRpYzMiLCJfb25PbmNlRGVjcyIsIl9vbk5vcm1hbERlY3MiLCJfb25jZUNvdW50RGVjcyIsIl9ub3JtYWxDb3VudERlY3MiLCJvbmNlQ291bnQiLCJub3JtYWxDb3VudCIsIm9uT25jZSIsIm9uTm9ybWFsIiwiQ2FwdHVyZUxpc3RlbmVyRWxlbWVudCIsIl9pbml0X29yZGVyIiwiX2luaXRQcm90bzQiLCJfaW5pdFN0YXRpYzQiLCJfb25DYXB0dXJlRGVjcyIsIl9vbkJ1YmJsZURlY3MiLCJfb3JkZXJEZWNzIiwiQXJyYXkiLCJjYXB0dXJlIiwib3JkZXIiLCJvbkNhcHR1cmUiLCJvbkJ1YmJsZSIsImNoaWxkIiwibGVuZ3RoIiwiTWl4ZWRPcHRpb25zRWxlbWVudCIsIl9pbml0X25vcm1hbENvdW50MiIsIl9pbml0X3Bhc3NpdmVDb3VudCIsIl9pbml0X29uY2VDb3VudDIiLCJfaW5pdFByb3RvNSIsIl9pbml0U3RhdGljNSIsIl9vbk5vcm1hbERlY3MyIiwiX29uUGFzc2l2ZURlY3MiLCJfb25PbmNlRGVjczIiLCJfbm9ybWFsQ291bnREZWNzMiIsIl9wYXNzaXZlQ291bnREZWNzIiwiX29uY2VDb3VudERlY3MyIiwicGFzc2l2ZSIsInBhc3NpdmVDb3VudCIsIm9uUGFzc2l2ZSJdLCJzb3VyY2VzIjpbInRlc3QtbGlzdGVuZXJzLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHksIGxpc3RlbmVyLCBodG1sIH0gZnJvbSAnLi4veC1lbGVtZW50LW5leHQuanMnO1xuXG5jbGFzcyBUZXN0RWxlbWVudENoaWxkIGV4dGVuZHMgSFRNTEVsZW1lbnQge1xuICBjb25uZWN0ZWRDYWxsYmFjaygpIHtcbiAgICBjb25zdCBldmVudFR5cGUgPSAnY29ubmVjdGVkJztcbiAgICBjb25zdCBldmVudERhdGEgPSB7IGJ1YmJsZXM6IHRydWUsIGNvbXBvc2VkOiB0cnVlIH07XG4gICAgdGhpcy5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChldmVudFR5cGUsIGV2ZW50RGF0YSkpO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1jaGlsZCcsIFRlc3RFbGVtZW50Q2hpbGQpO1xuXG5jbGFzcyBUZXN0RWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBOdW1iZXIsXG4gICAgZGVmYXVsdDogMCxcbiAgfSlcbiAgYWNjZXNzb3IgY2xpY2tzO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogTnVtYmVyLFxuICAgIGRlZmF1bHQ6IDAsXG4gIH0pXG4gIGFjY2Vzc29yIGNvbm5lY3Rpb25zO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogTnVtYmVyLFxuICAgIGRlZmF1bHQ6IDAsXG4gIH0pXG4gIGFjY2Vzc29yIGNvdW50O1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogTnVtYmVyLFxuICAgIGRlZmF1bHQ6IDAsXG4gIH0pXG4gIGFjY2Vzc29yIGN1c3RvbUV2ZW50Q291bnQ7XG5cbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBOdW1iZXIsXG4gICAgZGVmYXVsdDogKCkgPT4gMCxcbiAgfSlcbiAgYWNjZXNzb3IgY3VzdG9tRXZlbnRPbmNlQ291bnQ7XG5cbiAgQGxpc3RlbmVyKCdjbGljaycpXG4gIHN0YXRpYyBvbkNsaWNrKGhvc3QsIGV2dCkge1xuICAgIGhvc3QuY2xpY2tzKys7XG4gICAgaWYgKGV2dC50YXJnZXQuaWQgPT09ICdpbmNyZW1lbnQnKSB7XG4gICAgICBob3N0LmNvdW50Kys7XG4gICAgfSBlbHNlIGlmIChldnQudGFyZ2V0LmlkID09PSAnZGVjcmVtZW50Jykge1xuICAgICAgaG9zdC5jb3VudC0tO1xuICAgIH1cbiAgfVxuXG4gIEBsaXN0ZW5lcignY29ubmVjdGVkJylcbiAgc3RhdGljIG9uQ29ubmVjdGVkKGhvc3QpIHtcbiAgICBob3N0LmNvbm5lY3Rpb25zKys7XG4gIH1cblxuICBzdGF0aWMgb25DdXN0b21FdmVudE9uY2UoaG9zdCkge1xuICAgIGlmICh0aGlzID09PSBUZXN0RWxlbWVudCAmJiBob3N0LmNvbnN0cnVjdG9yID09PSBUZXN0RWxlbWVudCkge1xuICAgICAgaG9zdC5jdXN0b21FdmVudE9uY2VDb3VudCsrO1xuICAgIH1cbiAgfVxuXG4gIHN0YXRpYyBvbkN1c3RvbUV2ZW50KGhvc3QpIHtcbiAgICBpZiAodGhpcyA9PT0gVGVzdEVsZW1lbnQgJiYgaG9zdC5jb25zdHJ1Y3RvciA9PT0gVGVzdEVsZW1lbnQpIHtcbiAgICAgIGhvc3QuY3VzdG9tRXZlbnRDb3VudCsrO1xuICAgIH1cbiAgfVxuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgXG4gICAgICA8YnV0dG9uIGlkPVwiaW5jcmVtZW50XCIgdHlwZT1cImJ1dHRvblwiPis8L2J1dHRvbj5cbiAgICAgIDxidXR0b24gaWQ9XCJkZWNyZW1lbnRcIiB0eXBlPVwiYnV0dG9uXCI+LTwvYnV0dG9uPlxuICAgICAgPHNwYW4+Y2xpY2tzOiAke2hvc3QuY2xpY2tzfSBjb3VudCAke2hvc3QuY291bnR9PC9zcGFuPlxuICAgICAgPGRpdiBpZD1cImN1c3RvbS1ldmVudC1lbWl0dGVyXCI+PC9kaXY+XG4gICAgICA8dGVzdC1lbGVtZW50LWNoaWxkIGlkPVwiY29ubmVjdGVkLWV2ZW50LWVtaXR0ZXJcIj48L3Rlc3QtZWxlbWVudC1jaGlsZD5cbiAgICBgO1xuICB9XG5cbiAgY29ubmVjdGVkQ2FsbGJhY2soKSB7XG4gICAgc3VwZXIuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAgICB0aGlzLmxpc3Rlbih0aGlzLnNoYWRvd1Jvb3QsICdjdXN0b20tZXZlbnQnLCB0aGlzLmNvbnN0cnVjdG9yLm9uQ3VzdG9tRXZlbnRPbmNlLCB7IG9uY2U6IHRydWUgfSk7XG4gICAgdGhpcy5saXN0ZW4odGhpcy5zaGFkb3dSb290LCAnY3VzdG9tLWV2ZW50JywgdGhpcy5jb25zdHJ1Y3Rvci5vbkN1c3RvbUV2ZW50KTtcbiAgfVxuXG4gIGRpc2Nvbm5lY3RlZENhbGxiYWNrKCkge1xuICAgIHN1cGVyLmRpc2Nvbm5lY3RlZENhbGxiYWNrKCk7XG4gICAgdGhpcy51bmxpc3Rlbih0aGlzLnNoYWRvd1Jvb3QsICdjdXN0b20tZXZlbnQnLCB0aGlzLmNvbnN0cnVjdG9yLm9uQ3VzdG9tRXZlbnRPbmNlLCB7IG9uY2U6IHRydWUgfSk7XG4gICAgdGhpcy51bmxpc3Rlbih0aGlzLnNoYWRvd1Jvb3QsICdjdXN0b20tZXZlbnQnLCB0aGlzLmNvbnN0cnVjdG9yLm9uQ3VzdG9tRXZlbnQpO1xuICB9XG59XG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1uZXh0JywgVGVzdEVsZW1lbnQpO1xuXG5pdCgndGVzdCBsaWZlY3ljbGUnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoZWwuY2xpY2tzID09PSAwICYmIGVsLmNvdW50ID09PSAwLCAnaW5pdGlhbGl6ZWQgYXMgZXhwZWN0ZWQnKTtcblxuICBlbC5jbGljaygpO1xuICBhc3NlcnQoZWwuY2xpY2tzID09PSAwICYmIGVsLmNvdW50ID09PSAwLCAnbGlzdGVucyBvbiBzaGFkb3dSb290LCBub3QgaG9zdCcpO1xuXG4gIGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2luY3JlbWVudCcpLmNsaWNrKCk7XG4gIGFzc2VydChlbC5jbGlja3MgPT09IDEgJiYgZWwuY291bnQgPT09IDEsICdsaXN0ZW5zIHRvIGV2ZW50cycpO1xuXG4gIGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2RlY3JlbWVudCcpLmNsaWNrKCk7XG4gIGFzc2VydChlbC5jbGlja3MgPT09IDIgJiYgZWwuY291bnQgPT09IDAsICd3b3JrcyBmb3IgZGVsZWdhdGVkIGhhbmRsaW5nJyk7XG5cbiAgZG9jdW1lbnQuYm9keS5yZW1vdmVDaGlsZChlbCk7XG4gIGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2luY3JlbWVudCcpLmNsaWNrKCk7XG4gIGFzc2VydChlbC5jbGlja3MgPT09IDIgJiYgZWwuY291bnQgPT09IDAsICdyZW1vdmVzIGxpc3RlbmVycyBvbiBkaXNjb25uZWN0Jyk7XG5cbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdpbmNyZW1lbnQnKS5jbGljaygpO1xuICBhc3NlcnQoZWwuY2xpY2tzID09PSAzICYmIGVsLmNvdW50ID09PSAxLCAnYWRkcyBiYWNrIGxpc3RlbmVycyBvbiByZWNvbm5lY3QnKTtcbn0pO1xuXG5pdCgndGVzdCBjb25uZWN0ZWRDYWxsYmFjayBsaWZlY3ljbGUnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBjb25zdCBldmVudEVtaXR0ZXIgPSBlbC5zaGFkb3dSb290LmdldEVsZW1lbnRCeUlkKCdjdXN0b20tZXZlbnQtZW1pdHRlcicpO1xuICBldmVudEVtaXR0ZXIuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ2N1c3RvbS1ldmVudCcsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG4gIGFzc2VydChlbC5jdXN0b21FdmVudE9uY2VDb3VudCA9PT0gMSk7XG4gIGFzc2VydChlbC5jdXN0b21FdmVudENvdW50ID09PSAxKTtcbiAgZXZlbnRFbWl0dGVyLmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCdjdXN0b20tZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQoZWwuY3VzdG9tRXZlbnRPbmNlQ291bnQgPT09IDEpO1xuICBhc3NlcnQoZWwuY3VzdG9tRXZlbnRDb3VudCA9PT0gMik7XG4gIGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZWwpO1xuICBldmVudEVtaXR0ZXIuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ2N1c3RvbS1ldmVudCcsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG4gIGFzc2VydChlbC5jdXN0b21FdmVudE9uY2VDb3VudCA9PT0gMSk7XG4gIGFzc2VydChlbC5jdXN0b21FdmVudENvdW50ID09PSAyKTtcbn0pO1xuXG5pdCgndGVzdCBtYW51YWwgbGlmZWN5Y2xlJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IGNvdW50ID0gMDtcbiAgY29uc3Qgb25NYW51YWxDaGVjayA9ICgpID0+IGNvdW50Kys7XG4gIGVsLmxpc3RlbihlbC5zaGFkb3dSb290LCAnbWFudWFsLWNoZWNrJywgb25NYW51YWxDaGVjayk7XG4gIGVsLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ21hbnVhbC1jaGVjaycpKTtcbiAgZWwuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgnbWFudWFsLWNoZWNrJykpO1xuICBhc3NlcnQoY291bnQgPT09IDIsICdsaXN0ZW5lciB3YXMgYWRkZWQnKTtcbiAgZWwudW5saXN0ZW4oZWwuc2hhZG93Um9vdCwgJ21hbnVhbC1jaGVjaycsIG9uTWFudWFsQ2hlY2spO1xuICBlbC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCdtYW51YWwtY2hlY2snKSk7XG4gIGFzc2VydChjb3VudCA9PT0gMiwgJ2xpc3RlbmVyIHdhcyByZW1vdmVkJyk7XG59KTtcblxuaXQoJ3Rlc3Qgc3luY2hyb25vdXMgZXZlbnQgaGFuZGxpbmcnLCAoKSA9PiB7XG4gIC8vIFRoaXMgaXMgc3VidGxlLCBidXQgaXQgdGVzdHMgdGhhdCBpZiBjaGlsZCBlbGVtZW50cyBlbWl0IGV2ZW50c1xuICAvLyAgc3luY2hyb25vdXNseSBpbiB0aGVpciBmaXJzdCByZW5kZXIsIGRlbGVnYXRlZCBldmVudCBsaXN0ZW5pbmcgZnJvbSB0aGVcbiAgLy8gIHBhcmVudCB3aWxsIHN0aWxsIHdvcmsuXG4gIGxldCBjb3VudCA9IDA7XG4gIGNvbnN0IGRvY3VtZW50TGlzdGVuZXIgPSAoKSA9PiBjb3VudCsrO1xuICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdjb25uZWN0ZWQnLCBkb2N1bWVudExpc3RlbmVyKTtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChjb3VudCA9PT0gMSk7XG4gIGFzc2VydChlbC5jb25uZWN0aW9ucyA9PT0gY291bnQpO1xufSk7XG5cbml0KCd0aHJvd3MgZm9yIGJhZCBlbGVtZW50IG9uIGxpc3RlbicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwubGlzdGVuKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCBlbGVtZW50IHBhc3NlZCB0byBsaXN0ZW4gKGV4cGVjdGVkIEV2ZW50VGFyZ2V0LCBnb3QgVW5kZWZpbmVkKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCd0aHJvd3MgZm9yIGJhZCB0eXBlIG9uIGxpc3RlbicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwubGlzdGVuKGVsKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHR5cGUgcGFzc2VkIHRvIGxpc3RlbiAoZXhwZWN0ZWQgU3RyaW5nLCBnb3QgVW5kZWZpbmVkKS4nO1xuICAgIG1lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xuICAgIHBhc3NlZCA9IGVycm9yLm1lc3NhZ2UgPT09IGV4cGVjdGVkO1xuICB9XG4gIGFzc2VydChwYXNzZWQsIG1lc3NhZ2UpO1xufSk7XG5cbml0KCd0aHJvd3MgZm9yIGJhZCBjYWxsYmFjayBvbiBsaXN0ZW4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLmxpc3RlbihlbC5zaGFkb3dSb290LCAndGVzdCcpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQgY2FsbGJhY2sgcGFzc2VkIHRvIGxpc3RlbiAoZXhwZWN0ZWQgRnVuY3Rpb24sIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3Rocm93cyBmb3IgYmFkIG9wdGlvbnMgb24gbGlzdGVuJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC5saXN0ZW4oZWwuc2hhZG93Um9vdCwgJ3Rlc3QnLCAoKSA9PiB7fSwgdHJ1ZSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCBvcHRpb25zIHBhc3NlZCB0byBsaXN0ZW4gKGV4cGVjdGVkIE9iamVjdCwgZ290IEJvb2xlYW4pLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3Rocm93cyBmb3IgYmFkIGVsZW1lbnQgb24gdW5saXN0ZW4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLnVubGlzdGVuKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCBlbGVtZW50IHBhc3NlZCB0byB1bmxpc3RlbiAoZXhwZWN0ZWQgRXZlbnRUYXJnZXQsIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3Rocm93cyBmb3IgYmFkIHR5cGUgb24gdW5saXN0ZW4nLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBsZXQgcGFzc2VkID0gZmFsc2U7XG4gIGxldCBtZXNzYWdlID0gJ25vIGVycm9yIHdhcyB0aHJvd24nO1xuICB0cnkge1xuICAgIGVsLnVubGlzdGVuKGVsKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjb25zdCBleHBlY3RlZCA9ICdVbmV4cGVjdGVkIHR5cGUgcGFzc2VkIHRvIHVubGlzdGVuIChleHBlY3RlZCBTdHJpbmcsIGdvdCBVbmRlZmluZWQpLic7XG4gICAgbWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgcGFzc2VkID0gZXJyb3IubWVzc2FnZSA9PT0gZXhwZWN0ZWQ7XG4gIH1cbiAgYXNzZXJ0KHBhc3NlZCwgbWVzc2FnZSk7XG59KTtcblxuaXQoJ3Rocm93cyBmb3IgYmFkIGNhbGxiYWNrIG9uIHVubGlzdGVuJywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcbiAgbGV0IHBhc3NlZCA9IGZhbHNlO1xuICBsZXQgbWVzc2FnZSA9ICdubyBlcnJvciB3YXMgdGhyb3duJztcbiAgdHJ5IHtcbiAgICBlbC51bmxpc3RlbihlbC5zaGFkb3dSb290LCAndGVzdCcpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNvbnN0IGV4cGVjdGVkID0gJ1VuZXhwZWN0ZWQgY2FsbGJhY2sgcGFzc2VkIHRvIHVubGlzdGVuIChleHBlY3RlZCBGdW5jdGlvbiwgZ290IFVuZGVmaW5lZCkuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG5pdCgndGhyb3dzIGZvciBiYWQgb3B0aW9ucyBvbiB1bmxpc3RlbicsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LWVsZW1lbnQtbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGxldCBwYXNzZWQgPSBmYWxzZTtcbiAgbGV0IG1lc3NhZ2UgPSAnbm8gZXJyb3Igd2FzIHRocm93bic7XG4gIHRyeSB7XG4gICAgZWwudW5saXN0ZW4oZWwuc2hhZG93Um9vdCwgJ3Rlc3QnLCAoKSA9PiB7fSwgdHJ1ZSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgY29uc3QgZXhwZWN0ZWQgPSAnVW5leHBlY3RlZCBvcHRpb25zIHBhc3NlZCB0byB1bmxpc3RlbiAoZXhwZWN0ZWQgT2JqZWN0LCBnb3QgQm9vbGVhbikuJztcbiAgICBtZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcbiAgICBwYXNzZWQgPSBlcnJvci5tZXNzYWdlID09PSBleHBlY3RlZDtcbiAgfVxuICBhc3NlcnQocGFzc2VkLCBtZXNzYWdlKTtcbn0pO1xuXG4vLyBUZXN0IG11bHRpcGxlIGxpc3RlbmVycyBmb3Igc2FtZSBldmVudCB0eXBlXG5jbGFzcyBNdWx0aUxpc3RlbmVyRWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gIGFjY2Vzc29yIGNvdW50MTtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDAgfSlcbiAgYWNjZXNzb3IgY291bnQyO1xuXG4gIEBsaXN0ZW5lcigndGVzdC1ldmVudCcpXG4gIHN0YXRpYyBvblRlc3RFdmVudDEoaG9zdCkge1xuICAgIGhvc3QuY291bnQxKys7XG4gIH1cblxuICBAbGlzdGVuZXIoJ3Rlc3QtZXZlbnQnKVxuICBzdGF0aWMgb25UZXN0RXZlbnQyKGhvc3QpIHtcbiAgICBob3N0LmNvdW50MisrO1xuICB9XG5cbiAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICByZXR1cm4gaHRtbGA8ZGl2PiR7aG9zdC5jb3VudDF9LSR7aG9zdC5jb3VudDJ9PC9kaXY+YDtcbiAgfVxufVxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LW11bHRpLWxpc3RlbmVyJywgTXVsdGlMaXN0ZW5lckVsZW1lbnQpO1xuXG5pdCgnbXVsdGlwbGUgbGlzdGVuZXJzIGZvciBzYW1lIGV2ZW50IHR5cGUnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1tdWx0aS1saXN0ZW5lcicpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChlbC5jb3VudDEgPT09IDAgJiYgZWwuY291bnQyID09PSAwLCAnY291bnRlcnMgc3RhcnQgYXQgMCcpO1xuXG4gIGVsLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ3Rlc3QtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQoZWwuY291bnQxID09PSAxICYmIGVsLmNvdW50MiA9PT0gMSwgJ2JvdGggbGlzdGVuZXJzIGZpcmUnKTtcblxuICBlbC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGVsLmNvdW50MSA9PT0gMiAmJiBlbC5jb3VudDIgPT09IDIsICdib3RoIGxpc3RlbmVycyBmaXJlIG9uIHJlcGVhdCcpO1xuXG4gIGVsLnJlbW92ZSgpO1xufSk7XG5cbi8vIFRlc3QgbGlzdGVuZXIgd2l0aCBvbmNlIG9wdGlvblxuY2xhc3MgT25jZUxpc3RlbmVyRWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gIGFjY2Vzc29yIG9uY2VDb3VudDtcblxuICBAcHJvcGVydHkoeyB0eXBlOiBOdW1iZXIsIGluaXRpYWw6IDAgfSlcbiAgYWNjZXNzb3Igbm9ybWFsQ291bnQ7XG5cbiAgQGxpc3RlbmVyKCd0ZXN0LWV2ZW50JywgeyBvbmNlOiB0cnVlIH0pXG4gIHN0YXRpYyBvbk9uY2UoaG9zdCkge1xuICAgIGhvc3Qub25jZUNvdW50Kys7XG4gIH1cblxuICBAbGlzdGVuZXIoJ3Rlc3QtZXZlbnQnKVxuICBzdGF0aWMgb25Ob3JtYWwoaG9zdCkge1xuICAgIGhvc3Qubm9ybWFsQ291bnQrKztcbiAgfVxuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgPGRpdj4ke2hvc3Qub25jZUNvdW50fS0ke2hvc3Qubm9ybWFsQ291bnR9PC9kaXY+YDtcbiAgfVxufVxuY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LW9uY2UtbGlzdGVuZXInLCBPbmNlTGlzdGVuZXJFbGVtZW50KTtcblxuaXQoJ2xpc3RlbmVyIHdpdGggb25jZSBvcHRpb24gZmlyZXMgb25jZSBvbmx5JywgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3Qtb25jZS1saXN0ZW5lcicpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG5cbiAgZWwuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgndGVzdC1ldmVudCcsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG4gIGFzc2VydChlbC5vbmNlQ291bnQgPT09IDEgJiYgZWwubm9ybWFsQ291bnQgPT09IDEsICdib3RoIGZpcmUgb24gZmlyc3QgZXZlbnQnKTtcblxuICBlbC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGVsLm9uY2VDb3VudCA9PT0gMSAmJiBlbC5ub3JtYWxDb3VudCA9PT0gMiwgJ29uY2UgbGlzdGVuZXIgc3RvcHMsIG5vcm1hbCBjb250aW51ZXMnKTtcblxuICBlbC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGVsLm9uY2VDb3VudCA9PT0gMSAmJiBlbC5ub3JtYWxDb3VudCA9PT0gMywgJ29uY2UgbGlzdGVuZXIgc3RpbGwgc3RvcHBlZCcpO1xuXG4gIGVsLnJlbW92ZSgpO1xufSk7XG5cbi8vIFRlc3QgbGlzdGVuZXIgd2l0aCBjYXB0dXJlIG9wdGlvblxuY2xhc3MgQ2FwdHVyZUxpc3RlbmVyRWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogQXJyYXksIGluaXRpYWw6ICgpID0+IFtdIH0pXG4gIGFjY2Vzc29yIG9yZGVyO1xuXG4gIEBsaXN0ZW5lcigndGVzdC1ldmVudCcsIHsgY2FwdHVyZTogdHJ1ZSB9KVxuICBzdGF0aWMgb25DYXB0dXJlKGhvc3QpIHtcbiAgICBob3N0Lm9yZGVyID0gWy4uLmhvc3Qub3JkZXIsICdjYXB0dXJlJ107XG4gIH1cblxuICBAbGlzdGVuZXIoJ3Rlc3QtZXZlbnQnKVxuICBzdGF0aWMgb25CdWJibGUoaG9zdCkge1xuICAgIGhvc3Qub3JkZXIgPSBbLi4uaG9zdC5vcmRlciwgJ2J1YmJsZSddO1xuICB9XG5cbiAgc3RhdGljIHRlbXBsYXRlKCkge1xuICAgIHJldHVybiBodG1sYDxkaXYgaWQ9XCJjaGlsZFwiPjwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1jYXB0dXJlLWxpc3RlbmVyJywgQ2FwdHVyZUxpc3RlbmVyRWxlbWVudCk7XG5cbml0KCdsaXN0ZW5lciB3aXRoIGNhcHR1cmUgb3B0aW9uIGZpcmVzIGluIGNhcHR1cmUgcGhhc2UnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1jYXB0dXJlLWxpc3RlbmVyJyk7XG4gIGRvY3VtZW50LmJvZHkuYXBwZW5kKGVsKTtcblxuICBjb25zdCBjaGlsZCA9IGVsLnNoYWRvd1Jvb3QuZ2V0RWxlbWVudEJ5SWQoJ2NoaWxkJyk7XG4gIGNoaWxkLmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcblxuICBhc3NlcnQoZWwub3JkZXIubGVuZ3RoID09PSAyLCAnYm90aCBsaXN0ZW5lcnMgZmlyZWQnKTtcbiAgYXNzZXJ0KGVsLm9yZGVyWzBdID09PSAnY2FwdHVyZScsICdjYXB0dXJlIGxpc3RlbmVyIGZpcmVkIGZpcnN0Jyk7XG4gIGFzc2VydChlbC5vcmRlclsxXSA9PT0gJ2J1YmJsZScsICdidWJibGUgbGlzdGVuZXIgZmlyZWQgc2Vjb25kJyk7XG5cbiAgZWwucmVtb3ZlKCk7XG59KTtcblxuLy8gVGVzdCBtdWx0aXBsZSBsaXN0ZW5lcnMgd2l0aCBkaWZmZXJlbnQgb3B0aW9uc1xuY2xhc3MgTWl4ZWRPcHRpb25zRWxlbWVudCBleHRlbmRzIFhFbGVtZW50IHtcbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gIGFjY2Vzc29yIG5vcm1hbENvdW50O1xuXG4gIEBwcm9wZXJ0eSh7IHR5cGU6IE51bWJlciwgaW5pdGlhbDogMCB9KVxuICBhY2Nlc3NvciBwYXNzaXZlQ291bnQ7XG5cbiAgQHByb3BlcnR5KHsgdHlwZTogTnVtYmVyLCBpbml0aWFsOiAwIH0pXG4gIGFjY2Vzc29yIG9uY2VDb3VudDtcblxuICBAbGlzdGVuZXIoJ3Rlc3QtZXZlbnQnKVxuICBzdGF0aWMgb25Ob3JtYWwoaG9zdCkge1xuICAgIGhvc3Qubm9ybWFsQ291bnQrKztcbiAgfVxuXG4gIEBsaXN0ZW5lcigndGVzdC1ldmVudCcsIHsgcGFzc2l2ZTogdHJ1ZSB9KVxuICBzdGF0aWMgb25QYXNzaXZlKGhvc3QpIHtcbiAgICBob3N0LnBhc3NpdmVDb3VudCsrO1xuICB9XG5cbiAgQGxpc3RlbmVyKCd0ZXN0LWV2ZW50JywgeyBvbmNlOiB0cnVlIH0pXG4gIHN0YXRpYyBvbk9uY2UoaG9zdCkge1xuICAgIGhvc3Qub25jZUNvdW50Kys7XG4gIH1cblxuICBzdGF0aWMgdGVtcGxhdGUoaG9zdCkge1xuICAgIHJldHVybiBodG1sYDxkaXY+JHtob3N0Lm5vcm1hbENvdW50fS0ke2hvc3QucGFzc2l2ZUNvdW50fS0ke2hvc3Qub25jZUNvdW50fTwvZGl2PmA7XG4gIH1cbn1cbmN1c3RvbUVsZW1lbnRzLmRlZmluZSgndGVzdC1taXhlZC1vcHRpb25zJywgTWl4ZWRPcHRpb25zRWxlbWVudCk7XG5cbml0KCdtdWx0aXBsZSBsaXN0ZW5lcnMgd2l0aCBkaWZmZXJlbnQgb3B0aW9ucycsICgpID0+IHtcbiAgY29uc3QgZWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCd0ZXN0LW1peGVkLW9wdGlvbnMnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuXG4gIGVsLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ3Rlc3QtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQoZWwubm9ybWFsQ291bnQgPT09IDEsICdub3JtYWwgbGlzdGVuZXIgZmlyZXMnKTtcbiAgYXNzZXJ0KGVsLnBhc3NpdmVDb3VudCA9PT0gMSwgJ3Bhc3NpdmUgbGlzdGVuZXIgZmlyZXMnKTtcbiAgYXNzZXJ0KGVsLm9uY2VDb3VudCA9PT0gMSwgJ29uY2UgbGlzdGVuZXIgZmlyZXMnKTtcblxuICBlbC5zaGFkb3dSb290LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KCd0ZXN0LWV2ZW50JywgeyBidWJibGVzOiB0cnVlIH0pKTtcbiAgYXNzZXJ0KGVsLm5vcm1hbENvdW50ID09PSAyLCAnbm9ybWFsIGxpc3RlbmVyIGZpcmVzIGFnYWluJyk7XG4gIGFzc2VydChlbC5wYXNzaXZlQ291bnQgPT09IDIsICdwYXNzaXZlIGxpc3RlbmVyIGZpcmVzIGFnYWluJyk7XG4gIGFzc2VydChlbC5vbmNlQ291bnQgPT09IDEsICdvbmNlIGxpc3RlbmVyIGRvZXMgbm90IGZpcmUgYWdhaW4nKTtcblxuICBlbC5yZW1vdmUoKTtcbn0pO1xuXG4vLyBUZXN0IGRpc2Nvbm5lY3Rpb24gcmVtb3ZlcyBhbGwgbXVsdGlwbGUgbGlzdGVuZXJzXG5pdCgnZGlzY29ubmVjdGlvbiByZW1vdmVzIGFsbCBtdWx0aXBsZSBsaXN0ZW5lcnMnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1tdWx0aS1saXN0ZW5lcicpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG5cbiAgZWwuc2hhZG93Um9vdC5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudCgndGVzdC1ldmVudCcsIHsgYnViYmxlczogdHJ1ZSB9KSk7XG4gIGFzc2VydChlbC5jb3VudDEgPT09IDEgJiYgZWwuY291bnQyID09PSAxLCAnYm90aCBsaXN0ZW5lcnMgd29yayB3aGVuIGNvbm5lY3RlZCcpO1xuXG4gIGVsLnJlbW92ZSgpO1xuICBlbC5jb3VudDEgPSAwO1xuICBlbC5jb3VudDIgPSAwO1xuXG4gIGVsLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ3Rlc3QtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQoZWwuY291bnQxID09PSAwICYmIGVsLmNvdW50MiA9PT0gMCwgJ25laXRoZXIgbGlzdGVuZXIgZmlyZXMgd2hlbiBkaXNjb25uZWN0ZWQnKTtcblxuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGVsLnNoYWRvd1Jvb3QuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoJ3Rlc3QtZXZlbnQnLCB7IGJ1YmJsZXM6IHRydWUgfSkpO1xuICBhc3NlcnQoZWwuY291bnQxID09PSAxICYmIGVsLmNvdW50MiA9PT0gMSwgJ2JvdGggbGlzdGVuZXJzIHdvcmsgYWdhaW4gd2hlbiByZWNvbm5lY3RlZCcpO1xuXG4gIGVsLnJlbW92ZSgpO1xufSk7XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLFNBQVNBLE1BQU0sRUFBRUMsRUFBRSxRQUFRLDJCQUEyQjtBQUN0RCxTQUFTQyxRQUFRLEVBQUVDLFFBQVEsRUFBRUMsUUFBUSxFQUFFQyxJQUFJLFFBQVEsc0JBQXNCO0FBRXpFLE1BQU1DLGdCQUFnQixTQUFTQyxXQUFXLENBQUM7RUFDekNDLGlCQUFpQkEsQ0FBQSxFQUFHO0lBQ2xCLE1BQU1DLFNBQVMsR0FBRyxXQUFXO0lBQzdCLE1BQU1DLFNBQVMsR0FBRztNQUFFQyxPQUFPLEVBQUUsSUFBSTtNQUFFQyxRQUFRLEVBQUU7SUFBSyxDQUFDO0lBQ25ELElBQUksQ0FBQ0MsYUFBYSxDQUFDLElBQUlDLFdBQVcsQ0FBQ0wsU0FBUyxFQUFFQyxTQUFTLENBQUMsQ0FBQztFQUMzRDtBQUNGO0FBQ0FLLGNBQWMsQ0FBQ0MsTUFBTSxDQUFDLG9CQUFvQixFQUFFVixnQkFBZ0IsQ0FBQztBQUU3RCxNQUFNVyxXQUFXLFNBQVNmLFFB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 \ No newline at end of file diff --git a/test-next/test-listeners.src.js b/test-next/test-listeners.src.js new file mode 100644 index 0000000..0879d8c --- /dev/null +++ b/test-next/test-listeners.src.js @@ -0,0 +1,463 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, listener, html } from '../x-element-next.js'; + +class TestElementChild extends HTMLElement { + connectedCallback() { + const eventType = 'connected'; + const eventData = { bubbles: true, composed: true }; + this.dispatchEvent(new CustomEvent(eventType, eventData)); + } +} +customElements.define('test-element-child', TestElementChild); + +class TestElement extends XElement { + @property({ + type: Number, + default: 0, + }) + accessor clicks; + + @property({ + type: Number, + default: 0, + }) + accessor connections; + + @property({ + type: Number, + default: 0, + }) + accessor count; + + @property({ + type: Number, + default: 0, + }) + accessor customEventCount; + + @property({ + type: Number, + default: () => 0, + }) + accessor customEventOnceCount; + + @listener('click') + static onClick(host, evt) { + host.clicks++; + if (evt.target.id === 'increment') { + host.count++; + } else if (evt.target.id === 'decrement') { + host.count--; + } + } + + @listener('connected') + static onConnected(host) { + host.connections++; + } + + static onCustomEventOnce(host) { + if (this === TestElement && host.constructor === TestElement) { + host.customEventOnceCount++; + } + } + + static onCustomEvent(host) { + if (this === TestElement && host.constructor === TestElement) { + host.customEventCount++; + } + } + + static template(host) { + return html` + + + clicks: ${host.clicks} count ${host.count} +
+ + `; + } + + connectedCallback() { + super.connectedCallback(); + this.listen(this.shadowRoot, 'custom-event', this.constructor.onCustomEventOnce, { once: true }); + this.listen(this.shadowRoot, 'custom-event', this.constructor.onCustomEvent); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.unlisten(this.shadowRoot, 'custom-event', this.constructor.onCustomEventOnce, { once: true }); + this.unlisten(this.shadowRoot, 'custom-event', this.constructor.onCustomEvent); + } +} +customElements.define('test-element-next', TestElement); + +it('test lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(el.clicks === 0 && el.count === 0, 'initialized as expected'); + + el.click(); + assert(el.clicks === 0 && el.count === 0, 'listens on shadowRoot, not host'); + + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 1 && el.count === 1, 'listens to events'); + + el.shadowRoot.getElementById('decrement').click(); + assert(el.clicks === 2 && el.count === 0, 'works for delegated handling'); + + document.body.removeChild(el); + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 2 && el.count === 0, 'removes listeners on disconnect'); + + document.body.append(el); + el.shadowRoot.getElementById('increment').click(); + assert(el.clicks === 3 && el.count === 1, 'adds back listeners on reconnect'); +}); + +it('test connectedCallback lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + const eventEmitter = el.shadowRoot.getElementById('custom-event-emitter'); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { bubbles: true })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 1); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { bubbles: true })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 2); + document.body.removeChild(el); + eventEmitter.dispatchEvent(new CustomEvent('custom-event', { bubbles: true })); + assert(el.customEventOnceCount === 1); + assert(el.customEventCount === 2); +}); + +it('test manual lifecycle', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let count = 0; + const onManualCheck = () => count++; + el.listen(el.shadowRoot, 'manual-check', onManualCheck); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + assert(count === 2, 'listener was added'); + el.unlisten(el.shadowRoot, 'manual-check', onManualCheck); + el.shadowRoot.dispatchEvent(new CustomEvent('manual-check')); + assert(count === 2, 'listener was removed'); +}); + +it('test synchronous event handling', () => { + // This is subtle, but it tests that if child elements emit events + // synchronously in their first render, delegated event listening from the + // parent will still work. + let count = 0; + const documentListener = () => count++; + document.addEventListener('connected', documentListener); + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(count === 1); + assert(el.connections === count); +}); + +it('throws for bad element on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(); + } catch (error) { + const expected = 'Unexpected element passed to listen (expected EventTarget, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad type on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el); + } catch (error) { + const expected = 'Unexpected type passed to listen (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad callback on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el.shadowRoot, 'test'); + } catch (error) { + const expected = 'Unexpected callback passed to listen (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad options on listen', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.listen(el.shadowRoot, 'test', () => {}, true); + } catch (error) { + const expected = 'Unexpected options passed to listen (expected Object, got Boolean).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad element on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(); + } catch (error) { + const expected = 'Unexpected element passed to unlisten (expected EventTarget, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad type on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el); + } catch (error) { + const expected = 'Unexpected type passed to unlisten (expected String, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad callback on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el.shadowRoot, 'test'); + } catch (error) { + const expected = 'Unexpected callback passed to unlisten (expected Function, got Undefined).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +it('throws for bad options on unlisten', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + let passed = false; + let message = 'no error was thrown'; + try { + el.unlisten(el.shadowRoot, 'test', () => {}, true); + } catch (error) { + const expected = 'Unexpected options passed to unlisten (expected Object, got Boolean).'; + message = error.message; + passed = error.message === expected; + } + assert(passed, message); +}); + +// Test multiple listeners for same event type +class MultiListenerElement extends XElement { + @property({ type: Number, initial: 0 }) + accessor count1; + + @property({ type: Number, initial: 0 }) + accessor count2; + + @listener('test-event') + static onTestEvent1(host) { + host.count1++; + } + + @listener('test-event') + static onTestEvent2(host) { + host.count2++; + } + + static template(host) { + return html`
${host.count1}-${host.count2}
`; + } +} +customElements.define('test-multi-listener', MultiListenerElement); + +it('multiple listeners for same event type', () => { + const el = document.createElement('test-multi-listener'); + document.body.append(el); + assert(el.count1 === 0 && el.count2 === 0, 'counters start at 0'); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners fire'); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.count1 === 2 && el.count2 === 2, 'both listeners fire on repeat'); + + el.remove(); +}); + +// Test listener with once option +class OnceListenerElement extends XElement { + @property({ type: Number, initial: 0 }) + accessor onceCount; + + @property({ type: Number, initial: 0 }) + accessor normalCount; + + @listener('test-event', { once: true }) + static onOnce(host) { + host.onceCount++; + } + + @listener('test-event') + static onNormal(host) { + host.normalCount++; + } + + static template(host) { + return html`
${host.onceCount}-${host.normalCount}
`; + } +} +customElements.define('test-once-listener', OnceListenerElement); + +it('listener with once option fires once only', () => { + const el = document.createElement('test-once-listener'); + document.body.append(el); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.onceCount === 1 && el.normalCount === 1, 'both fire on first event'); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.onceCount === 1 && el.normalCount === 2, 'once listener stops, normal continues'); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.onceCount === 1 && el.normalCount === 3, 'once listener still stopped'); + + el.remove(); +}); + +// Test listener with capture option +class CaptureListenerElement extends XElement { + @property({ type: Array, initial: () => [] }) + accessor order; + + @listener('test-event', { capture: true }) + static onCapture(host) { + host.order = [...host.order, 'capture']; + } + + @listener('test-event') + static onBubble(host) { + host.order = [...host.order, 'bubble']; + } + + static template() { + return html`
`; + } +} +customElements.define('test-capture-listener', CaptureListenerElement); + +it('listener with capture option fires in capture phase', () => { + const el = document.createElement('test-capture-listener'); + document.body.append(el); + + const child = el.shadowRoot.getElementById('child'); + child.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + + assert(el.order.length === 2, 'both listeners fired'); + assert(el.order[0] === 'capture', 'capture listener fired first'); + assert(el.order[1] === 'bubble', 'bubble listener fired second'); + + el.remove(); +}); + +// Test multiple listeners with different options +class MixedOptionsElement extends XElement { + @property({ type: Number, initial: 0 }) + accessor normalCount; + + @property({ type: Number, initial: 0 }) + accessor passiveCount; + + @property({ type: Number, initial: 0 }) + accessor onceCount; + + @listener('test-event') + static onNormal(host) { + host.normalCount++; + } + + @listener('test-event', { passive: true }) + static onPassive(host) { + host.passiveCount++; + } + + @listener('test-event', { once: true }) + static onOnce(host) { + host.onceCount++; + } + + static template(host) { + return html`
${host.normalCount}-${host.passiveCount}-${host.onceCount}
`; + } +} +customElements.define('test-mixed-options', MixedOptionsElement); + +it('multiple listeners with different options', () => { + const el = document.createElement('test-mixed-options'); + document.body.append(el); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.normalCount === 1, 'normal listener fires'); + assert(el.passiveCount === 1, 'passive listener fires'); + assert(el.onceCount === 1, 'once listener fires'); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.normalCount === 2, 'normal listener fires again'); + assert(el.passiveCount === 2, 'passive listener fires again'); + assert(el.onceCount === 1, 'once listener does not fire again'); + + el.remove(); +}); + +// Test disconnection removes all multiple listeners +it('disconnection removes all multiple listeners', () => { + const el = document.createElement('test-multi-listener'); + document.body.append(el); + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners work when connected'); + + el.remove(); + el.count1 = 0; + el.count2 = 0; + + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.count1 === 0 && el.count2 === 0, 'neither listener fires when disconnected'); + + document.body.append(el); + el.shadowRoot.dispatchEvent(new CustomEvent('test-event', { bubbles: true })); + assert(el.count1 === 1 && el.count2 === 1, 'both listeners work again when reconnected'); + + el.remove(); +}); diff --git a/test-next/test-observed-properties.html b/test-next/test-observed-properties.html new file mode 100644 index 0000000..65b0f8a --- /dev/null +++ b/test-next/test-observed-properties.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Observed Properties

+ + + diff --git a/test-next/test-observed-properties.js b/test-next/test-observed-properties.js new file mode 100644 index 0000000..8242faa --- /dev/null +++ b/test-next/test-observed-properties.js @@ -0,0 +1,315 @@ +let _initProto, _aDecs, _init_a, _bDecs, _init_b, _cDecs, _init_c, _changesDecs, _init_changes, _poppedDecs, _init_popped, _asyncDecs, _init_async; +function _applyDecs(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; +class TestElement extends XElement { + static { + [_init_a, _init_b, _init_c, _init_changes, _init_popped, _init_async, _initProto] = _applyDecs(this, [[_aDecs, 1, "a"], [_bDecs, 1, "b"], [_cDecs, 1, "c"], [_changesDecs, 1, "changes"], [_poppedDecs, 1, "popped"], [_asyncDecs, 1, "async"]], [], 0, void 0, XElement).e; + } + static get [(_aDecs = property({ + type: String, + observe: (host, value, oldValue) => TestElement.#observeA(host, value, oldValue) + }), _bDecs = property({ + type: String, + observe: (host, value, oldValue) => TestElement.#observeB(host, value, oldValue) + }), _cDecs = property({ + type: String, + input: ['a', 'b'], + compute: (a, b) => TestElement.#computeC(a, b), + observe: (host, value, oldValue) => TestElement.#observeC(host, value, oldValue) + }), _changesDecs = property({ + type: Array, + observe: () => {} + }), _poppedDecs = property({ + type: Boolean, + reflect: true, + observe: (host, value, oldValue) => TestElement.#observePopped(host, value, oldValue) + }), _asyncDecs = property({ + observe: async () => {} + }), "styles")]() { + const styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(`\ + :host #container { + transition-property: box-shadow; + transition-duration: 300ms; + transition-timing-function: linear; + box-shadow: 0 0 0 1px black; + padding: 10px; + } + + :host([popped]) #container { + box-shadow: 0 0 10px 0 black; + } + `); + return [styleSheet]; + } + #A = (_initProto(this), _init_a(this)); + get a() { + return this.#A; + } + set a(v) { + this.#A = v; + } + #B = _init_b(this); + get b() { + return this.#B; + } + set b(v) { + this.#B = v; + } + #C = _init_c(this); + get c() { + return this.#C; + } + set c(v) { + this.#C = v; + } + #D = _init_changes(this); + get changes() { + return this.#D; + } + set changes(v) { + this.#D = v; + } + #E = _init_popped(this); + get popped() { + return this.#E; + } + set popped(v) { + this.#E = v; + } + #F = _init_async(this); + get async() { + return this.#F; + } + set async(v) { + this.#F = v; + } + static #computeC(a, b) { + return `${a} ${b}`; + } + static #observeA(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ + property: 'a', + value, + oldValue + }); + host.changes = changes; + } + static #observeB(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ + property: 'b', + value, + oldValue + }); + host.changes = changes; + } + static #observeC(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ + property: 'c', + value, + oldValue + }); + host.changes = changes; + } + static #observePopped(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ + property: 'popped', + value, + oldValue + }); + host.changes = changes; + } + static template(host) { + return html` +
+
Changes:
+
    + ${(host.changes || []).map(change => { + return html` +
  • + ${change.property}: "${change.oldValue}" → "${change.value}" +
  • + `; + })} +
+
+ `; + } +} +customElements.define('test-element-next', TestElement); +const isObject = obj => obj instanceof Object && obj !== null; +const deepEqual = (a, b) => { + if (a === b) { + return true; + } + return isObject(a) && isObject(b) && + // Note, we ignore non-enumerable properties (Symbols) here. + Object.keys(a).length === Object.keys(b).length && Object.keys(a).every(key => deepEqual(a[key], b[key])); +}; +it('initialized as expected', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert(deepEqual(el.changes, [{ + property: 'c', + value: 'undefined undefined', + oldValue: undefined + }]), 'initialized as expected'); + document.body.removeChild(el); +}); +it('x-element observed properties', async () => { + const el = document.createElement('test-element-next'); + el.a = '11'; + el.b = '22'; + document.body.append(el); + assert(deepEqual(el.changes, [{ + property: 'a', + value: '11', + oldValue: undefined + }, { + property: 'b', + value: '22', + oldValue: undefined + }, { + property: 'c', + value: '11 22', + oldValue: undefined + }]), 'initialized as expected'); + el.b = 'hey'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(deepEqual(el.changes, [{ + property: 'a', + value: '11', + oldValue: undefined + }, { + property: 'b', + value: '22', + oldValue: undefined + }, { + property: 'c', + value: '11 22', + oldValue: undefined + }, { + property: 'c', + value: '11 hey', + oldValue: '11 22' + }, { + property: 'b', + value: 'hey', + oldValue: '22' + }]), 'observe callbacks are called when properties change'); + el.b = 'hey'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(deepEqual(el.changes, [{ + property: 'a', + value: '11', + oldValue: undefined + }, { + property: 'b', + value: '22', + oldValue: undefined + }, { + property: 'c', + value: '11 22', + oldValue: undefined + }, { + property: 'c', + value: '11 hey', + oldValue: '11 22' + }, { + property: 'b', + value: 'hey', + oldValue: '22' + }]), 'observe callbacks are not called when set property is the same'); + el.popped = true; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + el.setAttribute('popped', 'still technically true'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert(deepEqual(el.changes, [{ + property: 'a', + value: '11', + oldValue: undefined + }, { + property: 'b', + value: '22', + oldValue: undefined + }, { + property: 'c', + value: '11 22', + oldValue: undefined + }, { + property: 'c', + value: '11 hey', + oldValue: '11 22' + }, { + property: 'b', + value: 'hey', + oldValue: '22' + }, { + property: 'popped', + value: true, + oldValue: undefined + }]), 'no re-entrance for observed, reflected properties'); +}); +it('child properties are bound before initialization', () => { + let _initProto2, _fooDecs, _init_foo, _initProto3, _fooDecs2, _init_foo2; + const observations = []; + class TestInner extends XElement { + static { + [_init_foo, _initProto2] = _applyDecs(this, [[_fooDecs, 1, "foo"]], [], 0, void 0, XElement).e; + } + #A = (_initProto2(this), _init_foo(this)); + get [(_fooDecs = property({ + type: Boolean, + observe: (host, value) => observations.push(value), + default: false + }), "foo")]() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + } + customElements.define('test-inner-next', TestInner); + class TestOuter extends XElement { + static { + [_init_foo2, _initProto3] = _applyDecs(this, [[_fooDecs2, 1, "foo"]], [], 0, void 0, XElement).e; + } + #A = (_initProto3(this), _init_foo2(this)); + get [(_fooDecs2 = property({ + type: Boolean, + default: true + }), "foo")]() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + static template(host) { + return html``; + } + } + customElements.define('test-outer-next', TestOuter); + const el = document.createElement('test-outer-next'); + document.body.append(el); + assert(observations[0] === true, observations[0]); + assert(observations.length === 1, observations); + el.remove(); +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJpdCIsIlhFbGVtZW50IiwicHJvcGVydHkiLCJodG1sIiwiVGVzdEVsZW1lbnQiLCJfaW5pdF9hIiwiX2luaXRfYiIsIl9pbml0X2MiLCJfaW5pdF9jaGFuZ2VzIiwiX2luaXRfcG9wcGVkIiwiX2luaXRfYXN5bmMiLCJfaW5pdFByb3RvIiwiX2FwcGx5RGVjcyIsIl9hRGVjcyIsIl9iRGVjcyIsIl9jRGVjcyIsIl9jaGFuZ2VzRGVjcyIsIl9wb3BwZWREZWNzIiwiX2FzeW5jRGVjcyIsImUiLCJ0eXBlIiwiU3RyaW5nIiwib2JzZXJ2ZSIsImhvc3QiLCJ2YWx1ZSIsIm9sZFZhbHVlIiwib2JzZXJ2ZUEiLCJvYnNlcnZlQiIsImlucHV0IiwiY29tcHV0ZSIsImEiLCJiIiwiY29tcHV0ZUMiLCJvYnNlcnZlQyIsIkFycmF5IiwiQm9vbGVhbiIsInJlZmxlY3QiLCJvYnNlcnZlUG9wcGVkIiwic3R5bGVTaGVldCIsIkNTU1N0eWxlU2hlZXQiLCJyZXBsYWNlU3luYyIsIkEiLCJ2IiwiQiIsIkMiLCJjIiwiRCIsImNoYW5nZXMiLCJFIiwicG9wcGVkIiwiRiIsImFzeW5jIiwiI2NvbXB1dGVDIiwiI29ic2VydmVBIiwiT2JqZWN0IiwiYXNzaWduIiwicHVzaCIsIiNvYnNlcnZlQiIsIiNvYnNlcnZlQyIsIiNvYnNlcnZlUG9wcGVkIiwidGVtcGxhdGUiLCJtYXAiLCJjaGFuZ2UiLCJjdXN0b21FbGVtZW50cyIsImRlZmluZSIsImlzT2JqZWN0Iiwib2JqIiwiZGVlcEVxdWFsIiwia2V5cyIsImxlbmd0aCIsImV2ZXJ5Iiwia2V5IiwiZWwiLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJib2R5IiwiYXBwZW5kIiwidW5kZWZpbmVkIiwicmVtb3ZlQ2hpbGQiLCJQcm9taXNlIiwicmVzb2x2ZSIsInNldEF0dHJpYnV0ZSIsIl9pbml0UHJvdG8yIiwiX2Zvb0RlY3MiLCJfaW5pdF9mb28iLCJfaW5pdFByb3RvMyIsIl9mb29EZWNzMiIsIl9pbml0X2ZvbzIiLCJvYnNlcnZhdGlvbnMiLCJUZXN0SW5uZXIiLCJkZWZhdWx0IiwiZm9vIiwiVGVzdE91dGVyIiwicmVtb3ZlIl0sInNvdXJjZXMiOlsidGVzdC1vYnNlcnZlZC1wcm9wZXJ0aWVzLnNyYy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBhc3NlcnQsIGl0IH0gZnJvbSAnQG5ldGZsaXgveC10ZXN0L3gtdGVzdC5qcyc7XG5pbXBvcnQgeyBYRWxlbWVudCwgcHJvcGVydHksIGh0bWwgfSBmcm9tICcuLi94LWVsZW1lbnQtbmV4dC5qcyc7XG5cbmNsYXNzIFRlc3RFbGVtZW50IGV4dGVuZHMgWEVsZW1lbnQge1xuICBzdGF0aWMgZ2V0IHN0eWxlcygpIHtcbiAgICBjb25zdCBzdHlsZVNoZWV0ID0gbmV3IENTU1N0eWxlU2hlZXQoKTtcbiAgICBzdHlsZVNoZWV0LnJlcGxhY2VTeW5jKGBcXFxuICAgICAgOmhvc3QgI2NvbnRhaW5lciB7XG4gICAgICAgIHRyYW5zaXRpb24tcHJvcGVydHk6IGJveC1zaGFkb3c7XG4gICAgICAgIHRyYW5zaXRpb24tZHVyYXRpb246IDMwMG1zO1xuICAgICAgICB0cmFuc2l0aW9uLXRpbWluZy1mdW5jdGlvbjogbGluZWFyO1xuICAgICAgICBib3gtc2hhZG93OiAwIDAgMCAxcHggYmxhY2s7XG4gICAgICAgIHBhZGRpbmc6IDEwcHg7XG4gICAgICB9XG5cbiAgICAgIDpob3N0KFtwb3BwZWRdKSAjY29udGFpbmVyIHtcbiAgICAgICAgYm94LXNoYWRvdzogMCAwIDEwcHggMCBibGFjaztcbiAgICAgIH1cbiAgICBgKTtcbiAgICByZXR1cm4gW3N0eWxlU2hlZXRdO1xuICB9XG5cbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBTdHJpbmcsXG4gICAgb2JzZXJ2ZTogKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSkgPT4gVGVzdEVsZW1lbnQuI29ic2VydmVBKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSksXG4gIH0pXG4gIGFjY2Vzc29yIGE7XG5cbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBTdHJpbmcsXG4gICAgb2JzZXJ2ZTogKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSkgPT4gVGVzdEVsZW1lbnQuI29ic2VydmVCKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSksXG4gIH0pXG4gIGFjY2Vzc29yIGI7XG5cbiAgQHByb3BlcnR5KHtcbiAgICB0eXBlOiBTdHJpbmcsXG4gICAgaW5wdXQ6IFsnYScsICdiJ10sXG4gICAgY29tcHV0ZTogKGEsIGIpID0+IFRlc3RFbGVtZW50LiNjb21wdXRlQyhhLCBiKSxcbiAgICBvYnNlcnZlOiAoaG9zdCwgdmFsdWUsIG9sZFZhbHVlKSA9PiBUZXN0RWxlbWVudC4jb2JzZXJ2ZUMoaG9zdCwgdmFsdWUsIG9sZFZhbHVlKSxcbiAgfSlcbiAgYWNjZXNzb3IgYztcblxuICBAcHJvcGVydHkoe1xuICAgIHR5cGU6IEFycmF5LFxuICAgIG9ic2VydmU6ICgpID0+IHt9LFxuICB9KVxuICBhY2Nlc3NvciBjaGFuZ2VzO1xuXG4gIEBwcm9wZXJ0eSh7XG4gICAgdHlwZTogQm9vbGVhbixcbiAgICByZWZsZWN0OiB0cnVlLFxuICAgIG9ic2VydmU6IChob3N0LCB2YWx1ZSwgb2xkVmFsdWUpID0+IFRlc3RFbGVtZW50LiNvYnNlcnZlUG9wcGVkKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSksXG4gIH0pXG4gIGFjY2Vzc29yIHBvcHBlZDtcblxuICBAcHJvcGVydHkoe1xuICAgIG9ic2VydmU6IGFzeW5jICgpID0+IHt9LFxuICB9KVxuICBhY2Nlc3NvciBhc3luYztcblxuICBzdGF0aWMgI2NvbXB1dGVDKGEsIGIpIHtcbiAgICByZXR1cm4gYCR7YX0gJHtifWA7XG4gIH1cblxuICBzdGF0aWMgI29ic2VydmVBKGhvc3QsIHZhbHVlLCBvbGRWYWx1ZSkge1xuICAgIGNvbnN0IGNoYW5nZXMgPSBPYmplY3QuYXNzaWduKFtdLCBob3N0LmNoYW5nZXMpO1xuICAgIGNoYW5nZXMucHVzaCh7IHByb3BlcnR5OiAnYScsIHZhbHVlLCBvbGRWYWx1ZSB9KTtcbiAgICBob3N0LmNoYW5nZXMgPSBjaGFuZ2VzO1xuICB9XG5cbiAgc3RhdGljICNvYnNlcnZlQihob3N0LCB2YWx1ZSwgb2xkVmFsdWUpIHtcbiAgICBjb25zdCBjaGFuZ2VzID0gT2JqZWN0LmFzc2lnbihbXSwgaG9zdC5jaGFuZ2VzKTtcbiAgICBjaGFuZ2VzLnB1c2goeyBwcm9wZXJ0eTogJ2InLCB2YWx1ZSwgb2xkVmFsdWUgfSk7XG4gICAgaG9zdC5jaGFuZ2VzID0gY2hhbmdlcztcbiAgfVxuXG4gIHN0YXRpYyAjb2JzZXJ2ZUMoaG9zdCwgdmFsdWUsIG9sZFZhbHVlKSB7XG4gICAgY29uc3QgY2hhbmdlcyA9IE9iamVjdC5hc3NpZ24oW10sIGhvc3QuY2hhbmdlcyk7XG4gICAgY2hhbmdlcy5wdXNoKHsgcHJvcGVydHk6ICdjJywgdmFsdWUsIG9sZFZhbHVlIH0pO1xuICAgIGhvc3QuY2hhbmdlcyA9IGNoYW5nZXM7XG4gIH1cblxuICBzdGF0aWMgI29ic2VydmVQb3BwZWQoaG9zdCwgdmFsdWUsIG9sZFZhbHVlKSB7XG4gICAgY29uc3QgY2hhbmdlcyA9IE9iamVjdC5hc3NpZ24oW10sIGhvc3QuY2hhbmdlcyk7XG4gICAgY2hhbmdlcy5wdXNoKHsgcHJvcGVydHk6ICdwb3BwZWQnLCB2YWx1ZSwgb2xkVmFsdWUgfSk7XG4gICAgaG9zdC5jaGFuZ2VzID0gY2hhbmdlcztcbiAgfVxuXG4gIHN0YXRpYyB0ZW1wbGF0ZShob3N0KSB7XG4gICAgcmV0dXJuIGh0bWxgXG4gICAgICA8ZGl2IGlkPVwiY29udGFpbmVyXCI+XG4gICAgICAgIDxkaXY+Q2hhbmdlczo8L2Rpdj5cbiAgICAgICAgPHVsPlxuICAgICAgICAgICR7KGhvc3QuY2hhbmdlcyB8fCBbXSkubWFwKChjaGFuZ2UgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIGh0bWxgXG4gICAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgICA8Y29kZT4ke2NoYW5nZS5wcm9wZXJ0eX08L2NvZGU+OiBcIiR7Y2hhbmdlLm9sZFZhbHVlfVwiICYjeDIxOTI7IFwiJHtjaGFuZ2UudmFsdWV9XCJcbiAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgIGA7XG4gICAgICAgICAgfSkpfVxuICAgICAgICA8L3VsPlxuICAgICAgPC9kaXY+XG4gICAgYDtcbiAgfVxufVxuXG5jdXN0b21FbGVtZW50cy5kZWZpbmUoJ3Rlc3QtZWxlbWVudC1uZXh0JywgVGVzdEVsZW1lbnQpO1xuXG5cbmNvbnN0IGlzT2JqZWN0ID0gb2JqID0+IG9iaiBpbnN0YW5jZW9mIE9iamVjdCAmJiBvYmogIT09IG51bGw7XG5jb25zdCBkZWVwRXF1YWwgPSAoYSwgYikgPT4ge1xuICBpZiAoYSA9PT0gYikge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG4gIHJldHVybiAoXG4gICAgaXNPYmplY3QoYSkgJiZcbiAgICBpc09iamVjdChiKSAmJlxuICAgIC8vIE5vdGUsIHdlIGlnbm9yZSBub24tZW51bWVyYWJsZSBwcm9wZXJ0aWVzIChTeW1ib2xzKSBoZXJlLlxuICAgIE9iamVjdC5rZXlzKGEpLmxlbmd0aCA9PT0gT2JqZWN0LmtleXMoYikubGVuZ3RoICYmXG4gICAgT2JqZWN0LmtleXMoYSkuZXZlcnkoa2V5ID0+IGRlZXBFcXVhbChhW2tleV0sIGJba2V5XSkpXG4gICk7XG59O1xuXG5pdCgnaW5pdGlhbGl6ZWQgYXMgZXhwZWN0ZWQnLCAoKSA9PiB7XG4gIGNvbnN0IGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgndGVzdC1lbGVtZW50LW5leHQnKTtcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuICBhc3NlcnQoXG4gICAgZGVlcEVxdWFsKGVsLmNoYW5nZXMsIFtcbiAgICAgIHsgcHJvcGVydHk6ICdjJywgdmFsdWU6ICd1bmRlZmluZWQgdW5kZWZpbmVkJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgIF0pLFxuICAgICdpbml0aWFsaXplZCBhcyBleHBlY3RlZCdcbiAgKTtcbiAgZG9jdW1lbnQuYm9keS5yZW1vdmVDaGlsZChlbCk7XG59KTtcblxuaXQoJ3gtZWxlbWVudCBvYnNlcnZlZCBwcm9wZXJ0aWVzJywgYXN5bmMgKCkgPT4ge1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3QtZWxlbWVudC1uZXh0Jyk7XG4gIGVsLmEgPSAnMTEnO1xuICBlbC5iID0gJzIyJztcbiAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZWwpO1xuXG4gIGFzc2VydChcbiAgICBkZWVwRXF1YWwoZWwuY2hhbmdlcywgW1xuICAgICAgeyBwcm9wZXJ0eTogJ2EnLCB2YWx1ZTogJzExJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ2InLCB2YWx1ZTogJzIyJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ2MnLCB2YWx1ZTogJzExIDIyJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgIF0pLFxuICAgICdpbml0aWFsaXplZCBhcyBleHBlY3RlZCdcbiAgKTtcblxuICBlbC5iID0gJ2hleSc7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KFxuICAgIGRlZXBFcXVhbChlbC5jaGFuZ2VzLCBbXG4gICAgICB7IHByb3BlcnR5OiAnYScsIHZhbHVlOiAnMTEnLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYicsIHZhbHVlOiAnMjInLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYycsIHZhbHVlOiAnMTEgMjInLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYycsIHZhbHVlOiAnMTEgaGV5Jywgb2xkVmFsdWU6ICcxMSAyMicgfSxcbiAgICAgIHsgcHJvcGVydHk6ICdiJywgdmFsdWU6ICdoZXknLCBvbGRWYWx1ZTogJzIyJyB9LFxuICAgIF0pLFxuICAgICdvYnNlcnZlIGNhbGxiYWNrcyBhcmUgY2FsbGVkIHdoZW4gcHJvcGVydGllcyBjaGFuZ2UnXG4gICk7XG5cbiAgZWwuYiA9ICdoZXknO1xuXG4gIC8vIFdlIG11c3QgYXdhaXQgYSBtaWNyb3Rhc2sgZm9yIHRoZSB1cGRhdGUgdG8gdGFrZSBwbGFjZS5cbiAgYXdhaXQgUHJvbWlzZS5yZXNvbHZlKCk7XG4gIGFzc2VydChcbiAgICBkZWVwRXF1YWwoZWwuY2hhbmdlcywgW1xuICAgICAgeyBwcm9wZXJ0eTogJ2EnLCB2YWx1ZTogJzExJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ2InLCB2YWx1ZTogJzIyJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ2MnLCB2YWx1ZTogJzExIDIyJywgb2xkVmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ2MnLCB2YWx1ZTogJzExIGhleScsIG9sZFZhbHVlOiAnMTEgMjInIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYicsIHZhbHVlOiAnaGV5Jywgb2xkVmFsdWU6ICcyMicgfSxcbiAgICBdKSxcbiAgICAnb2JzZXJ2ZSBjYWxsYmFja3MgYXJlIG5vdCBjYWxsZWQgd2hlbiBzZXQgcHJvcGVydHkgaXMgdGhlIHNhbWUnXG4gICk7XG5cbiAgZWwucG9wcGVkID0gdHJ1ZTtcblxuICAvLyBXZSBtdXN0IGF3YWl0IGEgbWljcm90YXNrIGZvciB0aGUgdXBkYXRlIHRvIHRha2UgcGxhY2UuXG4gIGF3YWl0IFByb21pc2UucmVzb2x2ZSgpO1xuICBlbC5zZXRBdHRyaWJ1dGUoJ3BvcHBlZCcsICdzdGlsbCB0ZWNobmljYWxseSB0cnVlJyk7XG5cbiAgLy8gV2UgbXVzdCBhd2FpdCBhIG1pY3JvdGFzayBmb3IgdGhlIHVwZGF0ZSB0byB0YWtlIHBsYWNlLlxuICBhd2FpdCBQcm9taXNlLnJlc29sdmUoKTtcbiAgYXNzZXJ0KFxuICAgIGRlZXBFcXVhbChlbC5jaGFuZ2VzLCBbXG4gICAgICB7IHByb3BlcnR5OiAnYScsIHZhbHVlOiAnMTEnLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYicsIHZhbHVlOiAnMjInLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYycsIHZhbHVlOiAnMTEgMjInLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgICB7IHByb3BlcnR5OiAnYycsIHZhbHVlOiAnMTEgaGV5Jywgb2xkVmFsdWU6ICcxMSAyMicgfSxcbiAgICAgIHsgcHJvcGVydHk6ICdiJywgdmFsdWU6ICdoZXknLCBvbGRWYWx1ZTogJzIyJyB9LFxuICAgICAgeyBwcm9wZXJ0eTogJ3BvcHBlZCcsIHZhbHVlOiB0cnVlLCBvbGRWYWx1ZTogdW5kZWZpbmVkIH0sXG4gICAgXSksXG4gICAgJ25vIHJlLWVudHJhbmNlIGZvciBvYnNlcnZlZCwgcmVmbGVjdGVkIHByb3BlcnRpZXMnXG4gICk7XG59KTtcblxuaXQoJ2NoaWxkIHByb3BlcnRpZXMgYXJlIGJvdW5kIGJlZm9yZSBpbml0aWFsaXphdGlvbicsICgpID0+IHtcbiAgY29uc3Qgb2JzZXJ2YXRpb25zID0gW107XG4gIGNsYXNzIFRlc3RJbm5lciBleHRlbmRzIFhFbGVtZW50IHtcbiAgICBAcHJvcGVydHkoe1xuICAgICAgdHlwZTogQm9vbGVhbixcbiAgICAgIG9ic2VydmU6IChob3N0LCB2YWx1ZSkgPT4gb2JzZXJ2YXRpb25zLnB1c2godmFsdWUpLFxuICAgICAgZGVmYXVsdDogZmFsc2UsXG4gICAgfSlcbiAgICBhY2Nlc3NvciBmb287XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LWlubmVyLW5leHQnLCBUZXN0SW5uZXIpO1xuICBjbGFzcyBUZXN0T3V0ZXIgZXh0ZW5kcyBYRWxlbWVudCB7XG4gICAgQHByb3BlcnR5KHtcbiAgICAgIHR5cGU6IEJvb2xlYW4sXG4gICAgICBkZWZhdWx0OiB0cnVlLFxuICAgIH0pXG4gICAgYWNjZXNzb3IgZm9vO1xuXG4gICAgc3RhdGljIHRlbXBsYXRlKGhvc3QpIHtcbiAgICAgIHJldHVybiBodG1sYDx0ZXN0LWlubmVyLW5leHQgLmZvbz1cIiR7aG9zdC5mb299XCI+PC90ZXN0LWlubmVyLW5leHQ+YDtcbiAgICB9XG4gIH1cbiAgY3VzdG9tRWxlbWVudHMuZGVmaW5lKCd0ZXN0LW91dGVyLW5leHQnLCBUZXN0T3V0ZXIpO1xuICBjb25zdCBlbCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ3Rlc3Qtb3V0ZXItbmV4dCcpO1xuICBkb2N1bWVudC5ib2R5LmFwcGVuZChlbCk7XG4gIGFzc2VydChvYnNlcnZhdGlvbnNbMF0gPT09IHRydWUsIG9ic2VydmF0aW9uc1swXSk7XG4gIGFzc2VydChvYnNlcnZhdGlvbnMubGVuZ3RoID09PSAxLCBvYnNlcnZhdGlvbnMpO1xuICBlbC5yZW1vdmUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxTQUFTQSxNQUFNLEVBQUVDLEVBQUUsUUFBUSwyQkFBMkI7QUFDdEQsU0FBU0MsUUFBUSxFQUFFQyxRQUFRLEVBQUVDLElBQUksUUFBUSxzQkFBc0I7QUFFL0QsTUFBTUMsV0FBVyxTQUFTSCxRQUFRLENBQUM7RUFBQTtJQUFBLENBQUFJLE9BQUEsRUFBQUMsT0FBQSxFQUFBQyxPQUFBLEVBQUFDLGFBQUEsRUFBQUMsWUFBQSxFQUFBQyxXQUFBLEVBQUFDLFVBQUEsSUFBQUMsVUFBQSxTQUFBQyxNQUFBLFlBQUFDLE1BQUEsWUFBQUMsTUFBQSxZQUFBQyxZQUFBLGtCQUFBQyxXQUFBLGlCQUFBQyxVQUFBLCtCQUFUakIsUUFBUSxFQUFBa0IsQ0FBQTtFQUFBO0VBQ2hDLGFBQUFOLE1BQUEsR0FrQkNYLFFBQVEsQ0FBQztJQUNSa0IsSUFBSSxFQUFFQyxNQUFNO0lBQ1pDLE9BQU8sRUFBRUEsQ0FBQ0MsSUFBSSxFQUFFQyxLQUFLLEVBQUVDLFFBQVEsS0FBS3JCLFdBQVcsQ0FBQyxDQUFDc0IsUUFBUSxD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 \ No newline at end of file diff --git a/test-next/test-observed-properties.src.js b/test-next/test-observed-properties.src.js new file mode 100644 index 0000000..3af80a5 --- /dev/null +++ b/test-next/test-observed-properties.src.js @@ -0,0 +1,230 @@ +import { assert, it } from '@netflix/x-test/x-test.js'; +import { XElement, property, html } from '../x-element-next.js'; + +class TestElement extends XElement { + static get styles() { + const styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(`\ + :host #container { + transition-property: box-shadow; + transition-duration: 300ms; + transition-timing-function: linear; + box-shadow: 0 0 0 1px black; + padding: 10px; + } + + :host([popped]) #container { + box-shadow: 0 0 10px 0 black; + } + `); + return [styleSheet]; + } + + @property({ + type: String, + observe: (host, value, oldValue) => TestElement.#observeA(host, value, oldValue), + }) + accessor a; + + @property({ + type: String, + observe: (host, value, oldValue) => TestElement.#observeB(host, value, oldValue), + }) + accessor b; + + @property({ + type: String, + input: ['a', 'b'], + compute: (a, b) => TestElement.#computeC(a, b), + observe: (host, value, oldValue) => TestElement.#observeC(host, value, oldValue), + }) + accessor c; + + @property({ + type: Array, + observe: () => {}, + }) + accessor changes; + + @property({ + type: Boolean, + reflect: true, + observe: (host, value, oldValue) => TestElement.#observePopped(host, value, oldValue), + }) + accessor popped; + + @property({ + observe: async () => {}, + }) + accessor async; + + static #computeC(a, b) { + return `${a} ${b}`; + } + + static #observeA(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ property: 'a', value, oldValue }); + host.changes = changes; + } + + static #observeB(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ property: 'b', value, oldValue }); + host.changes = changes; + } + + static #observeC(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ property: 'c', value, oldValue }); + host.changes = changes; + } + + static #observePopped(host, value, oldValue) { + const changes = Object.assign([], host.changes); + changes.push({ property: 'popped', value, oldValue }); + host.changes = changes; + } + + static template(host) { + return html` +
+
Changes:
+
    + ${(host.changes || []).map((change => { + return html` +
  • + ${change.property}: "${change.oldValue}" → "${change.value}" +
  • + `; + }))} +
+
+ `; + } +} + +customElements.define('test-element-next', TestElement); + + +const isObject = obj => obj instanceof Object && obj !== null; +const deepEqual = (a, b) => { + if (a === b) { + return true; + } + return ( + isObject(a) && + isObject(b) && + // Note, we ignore non-enumerable properties (Symbols) here. + Object.keys(a).length === Object.keys(b).length && + Object.keys(a).every(key => deepEqual(a[key], b[key])) + ); +}; + +it('initialized as expected', () => { + const el = document.createElement('test-element-next'); + document.body.append(el); + assert( + deepEqual(el.changes, [ + { property: 'c', value: 'undefined undefined', oldValue: undefined }, + ]), + 'initialized as expected' + ); + document.body.removeChild(el); +}); + +it('x-element observed properties', async () => { + const el = document.createElement('test-element-next'); + el.a = '11'; + el.b = '22'; + document.body.append(el); + + assert( + deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + ]), + 'initialized as expected' + ); + + el.b = 'hey'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert( + deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + ]), + 'observe callbacks are called when properties change' + ); + + el.b = 'hey'; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert( + deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + ]), + 'observe callbacks are not called when set property is the same' + ); + + el.popped = true; + + // We must await a microtask for the update to take place. + await Promise.resolve(); + el.setAttribute('popped', 'still technically true'); + + // We must await a microtask for the update to take place. + await Promise.resolve(); + assert( + deepEqual(el.changes, [ + { property: 'a', value: '11', oldValue: undefined }, + { property: 'b', value: '22', oldValue: undefined }, + { property: 'c', value: '11 22', oldValue: undefined }, + { property: 'c', value: '11 hey', oldValue: '11 22' }, + { property: 'b', value: 'hey', oldValue: '22' }, + { property: 'popped', value: true, oldValue: undefined }, + ]), + 'no re-entrance for observed, reflected properties' + ); +}); + +it('child properties are bound before initialization', () => { + const observations = []; + class TestInner extends XElement { + @property({ + type: Boolean, + observe: (host, value) => observations.push(value), + default: false, + }) + accessor foo; + } + customElements.define('test-inner-next', TestInner); + class TestOuter extends XElement { + @property({ + type: Boolean, + default: true, + }) + accessor foo; + + static template(host) { + return html``; + } + } + customElements.define('test-outer-next', TestOuter); + const el = document.createElement('test-outer-next'); + document.body.append(el); + assert(observations[0] === true, observations[0]); + assert(observations.length === 1, observations); + el.remove(); +}); diff --git a/test-next/test-parser.html b/test-next/test-parser.html new file mode 100644 index 0000000..515fb0d --- /dev/null +++ b/test-next/test-parser.html @@ -0,0 +1,18 @@ + + + + + + + + +

Test Parser

+ + + diff --git a/test-next/test-parser.js b/test-next/test-parser.js new file mode 100644 index 0000000..652535e --- /dev/null +++ b/test-next/test-parser.js @@ -0,0 +1,1686 @@ +import { assert, describe, it } from '@netflix/x-test/x-test.js'; +import { XParser } from '../../x-parser.js'; + +// Special symbol to hang test information off of. +const TEST = Symbol(); + +// Placeholder for values which delimit tagged template functions. These are +// not considered by the parser, so the value here has no significance and will +// not show up in any of the resulting fragments from the initial parse. +const VALUE = '…'; + +// Helper to stringify tokens the way we want to write them in our assertions. +const escape = string => { + return string + .replaceAll('\'', '\\\'') + .replaceAll('\n', '\\n') + .replaceAll('\\u', '\\\\u'); +}; +const stringifyObject = object => { + if (object === null) { + return 'null'; + } + let text = '{ '; + const keys = [...Object.keys(object)]; + let index = 0; + for (const key of keys) { + const value = object[key]; + switch (typeof value) { + case 'object': + text += `${key}: ${stringifyObject(value)}`; + break; + case 'string': + text += `${key}: '${escape(value)}'`; + break; + case 'undefined': + case 'number': + case 'boolean': + text += `${key}: ${value}`; + break; + default: + // Make sure we print _something_ for debugging even if it’s wrong. + text += `${key}: "${value}"`; + } + if (index++ < keys.length - 1) { + text += ', '; + } + } + text += ' }'; + return text; +}; +const stringifyTokens = tokens => { + const lines = []; + for (const token of tokens) { + lines.push(` ${stringifyObject(token)},`); + } + return `[\n${lines.join('\n')}\n]`; +}; + +// Helper for figuring out if the tokens we got were correct. +const isObject = obj => obj instanceof Object && obj !== null; +const deepEqual = (a, b) => { + if (a === b) { + return true; + } + return ( + isObject(a) && + isObject(b) && + // Note, we ignore non-enumerable properties (Symbols) here. + Object.keys(a).length === Object.keys(b).length && + Object.keys(a).every(key => deepEqual(a[key], b[key])) + ); +}; + +// Simple helper for asserting thrown messages. +const assertThrows = (callback, expectedMessage, options) => { + let thrown = false; + try { + callback(); + } catch (error) { + thrown = true; + if (options?.startsWith === true) { + assert(error.message.startsWith(expectedMessage), error.message); + } else { + assert(error.message === expectedMessage, error.message); + } + } + assert(thrown, 'no error was thrown'); +}; + +// Simpler helper to add some test information under a TEST symbol. +const wrapper = strings => { + const tokens = []; + const onToken = (type, index, start, end, substring) => { + tokens.push({ type, index, start, end, substring }); + }; + XParser.parse(strings, onToken); + tokens[TEST] = { strings }; + return tokens; +}; + +// Simple function to return strings array from tagged template function call. +// Since IDEs will pick up on the “html” syntax, they should highlight, which +// will make this more readable. +const html = strings => wrapper(strings); + +// Certain tests require that we write _terribly broken_ html. To prevent IDEs +// from choking when trying to highlight, we also have a “htmlol” function. +const htmlol = strings => wrapper(strings); + +describe('basic', () => { + it('empty template works', () => { + const expectedTokens = []; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('single string works', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 4, end: 5, substring: '>' }, + { type: 'text-start', index: 0, start: 5, end: 5, substring: '' }, + { type: 'text-plaintext', index: 0, start: 5, end: 22, substring: 'No interpolation.' }, + { type: 'text-end', index: 0, start: 22, end: 22, substring: '' }, + { type: 'end-tag-open', index: 0, start: 22, end: 24, substring: '' }, + ]; + const tokens = html`
No interpolation.
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('multi-line string works', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-plaintext', index: 0, start: 0, end: 37, substring: '\n one\n two\n three\n ' }, + { type: 'text-end', index: 0, start: 37, end: 37, substring: '' }, + ]; + const tokens = html` + one + two + three + `; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('comments', () => { + it('comments work', () => { + const expectedTokens = [ + { type: 'comment-open', index: 0, start: 0, end: 4, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('multi-line comments work', () => { + const expectedTokens = [ + { type: 'comment-open', index: 0, start: 0, end: 4, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('character references', () => { + it('accepts references in replaceable character data', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 4, end: 5, substring: '>' }, + { type: 'text-start', index: 0, start: 5, end: 5, substring: '' }, + { type: 'text-reference', index: 0, start: 5, end: 11, substring: '{' }, + { type: 'text-plaintext', index: 0, start: 11, end: 13, substring: '--' }, + { type: 'text-reference', index: 0, start: 13, end: 17, substring: '<' }, + { type: 'text-reference', index: 0, start: 17, end: 22, substring: '&' }, + { type: 'text-plaintext', index: 0, start: 22, end: 24, substring: '--' }, + { type: 'text-reference', index: 0, start: 24, end: 28, substring: '>' }, + { type: 'text-reference', index: 0, start: 28, end: 34, substring: ''' }, + { type: 'text-reference', index: 0, start: 34, end: 40, substring: '"' }, + { type: 'text-plaintext', index: 0, start: 40, end: 42, substring: '--' }, + { type: 'text-reference', index: 0, start: 42, end: 50, substring: '}' }, + { type: 'text-end', index: 0, start: 50, end: 50, substring: '' }, + { type: 'end-tag-open', index: 0, start: 50, end: 52, substring: '' }, + ]; + const tokens = html`
{--<&-->'"--}
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('accepts references in replaceable character data for special syntax', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-reference', index: 0, start: 0, end: 5, substring: '&' }, + { type: 'text-reference', index: 0, start: 5, end: 9, substring: '<' }, + { type: 'text-reference', index: 0, start: 9, end: 13, substring: '>' }, + { type: 'text-reference', index: 0, start: 13, end: 19, substring: '"' }, + { type: 'text-reference', index: 0, start: 19, end: 25, substring: ''' }, + { type: 'text-end', index: 0, start: 25, end: 25, substring: '' }, + ]; + const tokens = html`&<>"'`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('accepts references for commonly-used characters', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-reference', index: 0, start: 0, end: 6, substring: ' ' }, + { type: 'text-reference', index: 0, start: 6, end: 13, substring: '‘' }, + { type: 'text-reference', index: 0, start: 13, end: 20, substring: '’' }, + { type: 'text-reference', index: 0, start: 20, end: 27, substring: '“' }, + { type: 'text-reference', index: 0, start: 27, end: 34, substring: '”' }, + { type: 'text-reference', index: 0, start: 34, end: 41, substring: '–' }, + { type: 'text-reference', index: 0, start: 41, end: 48, substring: '—' }, + { type: 'text-reference', index: 0, start: 48, end: 56, substring: '…' }, + { type: 'text-reference', index: 0, start: 56, end: 62, substring: '•' }, + { type: 'text-reference', index: 0, start: 62, end: 70, substring: '·' }, + { type: 'text-reference', index: 0, start: 70, end: 78, substring: '†' }, + { type: 'text-end', index: 0, start: 78, end: 78, substring: '' }, + ]; + const tokens = html` ‘’“”–—…•·†`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('accepts references in attribute values', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'attribute-name', index: 0, start: 5, end: 8, substring: 'foo' }, + { type: 'start-tag-equals', index: 0, start: 8, end: 9, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 9, end: 10, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 10, end: 10, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 10, end: 12, substring: '--' }, + { type: 'attribute-value-reference', index: 0, start: 12, end: 18, substring: '{' }, + { type: 'attribute-value-reference', index: 0, start: 18, end: 22, substring: '<' }, + { type: 'attribute-value-reference', index: 0, start: 22, end: 27, substring: '&' }, + { type: 'attribute-value-reference', index: 0, start: 27, end: 31, substring: '>' }, + { type: 'attribute-value-reference', index: 0, start: 31, end: 37, substring: ''' }, + { type: 'attribute-value-reference', index: 0, start: 37, end: 43, substring: '"' }, + { type: 'attribute-value-reference', index: 0, start: 43, end: 51, substring: '}' }, + { type: 'attribute-value-plaintext', index: 0, start: 51, end: 53, substring: '--' }, + { type: 'attribute-value-end', index: 0, start: 53, end: 53, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 53, end: 54, substring: '"' }, + { type: 'start-tag-close', index: 0, start: 54, end: 55, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 55, end: 57, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('accepts named references which require surrogate pairs', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 4, end: 5, substring: '>' }, + { type: 'text-start', index: 0, start: 5, end: 5, substring: '' }, + { type: 'text-plaintext', index: 0, start: 5, end: 7, substring: '--' }, + { type: 'text-reference', index: 0, start: 7, end: 13, substring: '𝕓' }, + { type: 'text-reference', index: 0, start: 13, end: 19, substring: '𝕓' }, + { type: 'text-plaintext', index: 0, start: 19, end: 21, substring: '--' }, + { type: 'text-reference', index: 0, start: 21, end: 27, substring: '𝕓' }, + { type: 'text-plaintext', index: 0, start: 27, end: 29, substring: '--' }, + { type: 'text-end', index: 0, start: 29, end: 29, substring: '' }, + { type: 'end-tag-open', index: 0, start: 29, end: 31, substring: '' }, + ]; + const tokens = html`
--𝕓𝕓--𝕓--
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('leaves malformed references as-is', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 4, end: 5, substring: '>' }, + { type: 'text-start', index: 0, start: 5, end: 5, substring: '' }, + { type: 'text-plaintext', index: 0, start: 5, end: 7, substring: '--' }, + { type: 'text-reference', index: 0, start: 7, end: 12, substring: '&:^);' }, + { type: 'text-plaintext', index: 0, start: 12, end: 14, substring: '--' }, + { type: 'text-end', index: 0, start: 14, end: 14, substring: '' }, + { type: 'end-tag-open', index: 0, start: 14, end: 16, substring: '' }, + ]; + const tokens = htmlol`
--&:^);--
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('JS-y escapes / encodings', () => { + it('properly encoded JS escape characters work', () => { + // Just checks that no errors are thrown. + html` + The \n is a newline. + The \r is a return. + The ', ", and ` characters are quotes and back ticks. + This \ is a literal backslash. + The \t character is a tab. + The \b is a backspace. + The \f is a form feed. + The \v character is a vertical tab. + The \u2026 character is an ellipsis (…). + The \x8230 character is also an ellipsis (…). + The \0 character is the nul character. + The ${interpolation} character is used to work around interpolations. + `; + }); +}); + +describe('attributes and properties', () => { + it('unbound attributes are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'attribute-name', index: 0, start: 5, end: 6, substring: 'f' }, + { type: 'start-tag-equals', index: 0, start: 6, end: 7, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 7, end: 8, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 8, end: 8, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 8, end: 9, substring: 'b' }, + { type: 'attribute-value-end', index: 0, start: 9, end: 9, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 9, end: 10, substring: '"' }, + { type: 'start-tag-close', index: 0, start: 10, end: 11, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 11, end: 13, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('bound attributes are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'bound-attribute-name', index: 0, start: 5, end: 9, substring: 'attr' }, + { type: 'start-tag-equals', index: 0, start: 9, end: 10, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 10, end: 11, substring: '"' }, + { type: 'bound-attribute-value', index: 0, start: 11, end: 11, substring: '' }, + { type: 'start-tag-quote', index: 1, start: 0, end: 1, substring: '"' }, + { type: 'start-tag-close', index: 1, start: 1, end: 2, substring: '>' }, + { type: 'end-tag-open', index: 1, start: 2, end: 4, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + // It’s also a good test that this is at the _end_ of the opening tag. If we + // change this, we should write another test to separately check that. + it('unbound boolean attributes are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'boolean-name', index: 0, start: 5, end: 8, substring: 'foo' }, + { type: 'start-tag-close', index: 0, start: 8, end: 9, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 9, end: 11, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('bound boolean attributes are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'bound-boolean-prefix', index: 0, start: 5, end: 6, substring: '?' }, + { type: 'bound-boolean-name', index: 0, start: 6, end: 9, substring: 'foo' }, + { type: 'start-tag-equals', index: 0, start: 9, end: 10, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 10, end: 11, substring: '"' }, + { type: 'bound-boolean-value', index: 0, start: 11, end: 11, substring: '' }, + { type: 'start-tag-quote', index: 1, start: 0, end: 1, substring: '"' }, + { type: 'start-tag-close', index: 1, start: 1, end: 2, substring: '>' }, + { type: 'end-tag-open', index: 1, start: 2, end: 4, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('bound defined attributes are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'bound-defined-prefix', index: 0, start: 5, end: 7, substring: '??' }, + { type: 'bound-defined-name', index: 0, start: 7, end: 10, substring: 'foo' }, + { type: 'start-tag-equals', index: 0, start: 10, end: 11, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 11, end: 12, substring: '"' }, + { type: 'bound-defined-value', index: 0, start: 12, end: 12, substring: '' }, + { type: 'start-tag-quote', index: 1, start: 0, end: 1, substring: '"' }, + { type: 'start-tag-close', index: 1, start: 1, end: 2, substring: '>' }, + { type: 'end-tag-open', index: 1, start: 2, end: 4, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('unbound “on*” attributes as event handlers work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'attribute-name', index: 0, start: 5, end: 12, substring: 'onclick' }, + { type: 'start-tag-equals', index: 0, start: 12, end: 13, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 13, end: 14, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 14, end: 14, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 14, end: 34, substring: 'this.textContent = \'' }, + { type: 'attribute-value-reference', index: 0, start: 34, end: 42, substring: '…' }, + { type: 'attribute-value-plaintext', index: 0, start: 42, end: 44, substring: 'hi' }, + { type: 'attribute-value-reference', index: 0, start: 44, end: 50, substring: '\' }, + { type: 'attribute-value-plaintext', index: 0, start: 50, end: 57, substring: 'u2026\';' }, + { type: 'attribute-value-end', index: 0, start: 57, end: 57, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 57, end: 58, substring: '"' }, + { type: 'start-tag-close', index: 0, start: 58, end: 59, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 59, end: 61, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('properties are reported', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-space', index: 0, start: 4, end: 5, substring: ' ' }, + { type: 'bound-property-prefix', index: 0, start: 5, end: 6, substring: '.' }, + { type: 'bound-property-name', index: 0, start: 6, end: 10, substring: 'prop' }, + { type: 'start-tag-equals', index: 0, start: 10, end: 11, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 11, end: 12, substring: '"' }, + { type: 'bound-property-value', index: 0, start: 12, end: 12, substring: '' }, + { type: 'start-tag-quote', index: 1, start: 0, end: 1, substring: '"' }, + { type: 'start-tag-close', index: 1, start: 1, end: 2, substring: '>' }, + { type: 'end-tag-open', index: 1, start: 2, end: 4, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('content interpolation', () => { + it('solo interpolation works', () => { + const expectedTokens = [ + { type: 'bound-content-value', index: 0, start: 0, end: 0, substring: '' }, + ]; + const tokens = html`${VALUE}`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('adjacent interpolations work', () => { + const expectedTokens = [ + { type: 'bound-content-value', index: 0, start: 0, end: 0, substring: '' }, + { type: 'bound-content-value', index: 1, start: 0, end: 0, substring: '' }, + ]; + const tokens = html`${VALUE}${VALUE}`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('odds and ends', () => { + it('surprisingly-accepted characters work', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-plaintext', index: 0, start: 0, end: 9, substring: '>\'"&& & &' }, + { type: 'text-end', index: 0, start: 9, end: 9, substring: '' }, + { type: 'start-tag-open', index: 0, start: 9, end: 10, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 10, end: 13, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 13, end: 14, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 14, end: 16, substring: '' }, + { type: 'text-start', index: 0, start: 20, end: 20, substring: '' }, + { type: 'text-plaintext', index: 0, start: 20, end: 21, substring: '&' }, + { type: 'text-end', index: 0, start: 21, end: 21, substring: '' }, + ]; + const tokens = html`>'"&& & &
&`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('elements with "/" characters in attributes work', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-plaintext', index: 0, start: 0, end: 7, substring: '\n ' }, + { type: 'text-end', index: 0, start: 7, end: 7, substring: '' }, + { type: 'start-tag-open', index: 0, start: 7, end: 8, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 8, end: 9, substring: 'a' }, + { type: 'start-tag-space', index: 0, start: 9, end: 18, substring: '\n ' }, + { type: 'attribute-name', index: 0, start: 18, end: 22, substring: 'href' }, + { type: 'start-tag-equals', index: 0, start: 22, end: 23, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 23, end: 24, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 24, end: 24, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 24, end: 60, substring: 'https://github.com/Netflix/x-element' }, + { type: 'attribute-value-end', index: 0, start: 60, end: 60, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 60, end: 61, substring: '"' }, + { type: 'start-tag-close', index: 0, start: 61, end: 62, substring: '>' }, + { type: 'text-start', index: 0, start: 62, end: 62, substring: '' }, + { type: 'text-plaintext', index: 0, start: 62, end: 86, substring: '\n click me\n ' }, + { type: 'text-end', index: 0, start: 86, end: 86, substring: '' }, + { type: 'end-tag-open', index: 0, start: 86, end: 88, substring: '' }, + { type: 'text-start', index: 0, start: 90, end: 90, substring: '' }, + { type: 'text-plaintext', index: 0, start: 90, end: 95, substring: '\n ' }, + { type: 'text-end', index: 0, start: 95, end: 95, substring: '' }, + ]; + // Note the "/" character. + const tokens = html` + + click me + + `; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('elements with "<" or ">" characters in attributes work', () => { + const expectedTokens = [ + { type: 'text-start', index: 0, start: 0, end: 0, substring: '' }, + { type: 'text-plaintext', index: 0, start: 0, end: 7, substring: '\n ' }, + { type: 'text-end', index: 0, start: 7, end: 7, substring: '' }, + { type: 'start-tag-open', index: 0, start: 7, end: 8, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 8, end: 9, substring: 'a' }, + { type: 'start-tag-space', index: 0, start: 9, end: 18, substring: '\n ' }, + { type: 'attribute-name', index: 0, start: 18, end: 26, substring: 'data-foo' }, + { type: 'start-tag-equals', index: 0, start: 26, end: 27, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 27, end: 28, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 28, end: 28, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 28, end: 38, substring: '<><>' }, + { type: 'attribute-value-end', index: 0, start: 38, end: 38, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 38, end: 39, substring: '"' }, + { type: 'start-tag-space', index: 0, start: 39, end: 48, substring: '\n ' }, + { type: 'attribute-name', index: 0, start: 48, end: 52, substring: 'href' }, + { type: 'start-tag-equals', index: 0, start: 52, end: 53, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 53, end: 54, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 54, end: 54, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 54, end: 90, substring: 'https://github.com/Netflix/x-element' }, + { type: 'attribute-value-end', index: 0, start: 90, end: 90, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 90, end: 91, substring: '"' }, + { type: 'start-tag-close', index: 0, start: 91, end: 92, substring: '>' }, + { type: 'text-start', index: 0, start: 92, end: 92, substring: '' }, + { type: 'text-plaintext', index: 0, start: 92, end: 116, substring: '\n click me\n ' }, + { type: 'text-end', index: 0, start: 116, end: 116, substring: '' }, + { type: 'end-tag-open', index: 0, start: 116, end: 118, substring: '' }, + { type: 'text-start', index: 0, start: 120, end: 120, substring: '' }, + { type: 'text-plaintext', index: 0, start: 120, end: 125, substring: '\n ' }, + { type: 'text-end', index: 0, start: 125, end: 125, substring: '' }, + ]; + // Note the "/", "<", and ">" characters. + const tokens = html` + + click me + + `; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('multiple opening and closing tags work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 4, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 4, end: 5, substring: '>' }, + { type: 'start-tag-open', index: 0, start: 5, end: 6, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 6, end: 9, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 9, end: 10, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 10, end: 12, substring: '' }, + { type: 'end-tag-open', index: 0, start: 16, end: 18, substring: '' }, + ]; + const tokens = html`
`; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('void elements work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 6, substring: 'input' }, + { type: 'start-tag-space', index: 0, start: 6, end: 7, substring: ' ' }, + { type: 'attribute-name', index: 0, start: 7, end: 11, substring: 'type' }, + { type: 'start-tag-equals', index: 0, start: 11, end: 12, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 12, end: 13, substring: '"' }, + { type: 'attribute-value-start', index: 0, start: 13, end: 13, substring: '' }, + { type: 'attribute-value-plaintext', index: 0, start: 13, end: 21, substring: 'checkbox' }, + { type: 'attribute-value-end', index: 0, start: 21, end: 21, substring: '' }, + { type: 'start-tag-quote', index: 0, start: 21, end: 22, substring: '"' }, + { type: 'start-tag-space', index: 0, start: 22, end: 23, substring: ' ' }, + { type: 'bound-attribute-name', index: 0, start: 23, end: 28, substring: 'value' }, + { type: 'start-tag-equals', index: 0, start: 28, end: 29, substring: '=' }, + { type: 'start-tag-quote', index: 0, start: 29, end: 30, substring: '"' }, + { type: 'bound-attribute-value', index: 0, start: 30, end: 30, substring: '' }, + { type: 'start-tag-quote', index: 1, start: 0, end: 1, substring: '"' }, + { type: 'void-tag-close', index: 1, start: 1, end: 2, substring: '>' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('textarea elements work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'textarea' }, + { type: 'start-tag-close', index: 0, start: 9, end: 10, substring: '>' }, + { type: 'text-start', index: 0, start: 10, end: 10, substring: '' }, + { type: 'text-plaintext', index: 0, start: 10, end: 31, substring: 'this is the ' }, + { type: 'text-reference', index: 0, start: 31, end: 38, substring: '“' }, + { type: 'text-plaintext', index: 0, start: 38, end: 45, substring: 'default' }, + { type: 'text-reference', index: 0, start: 45, end: 52, substring: '”' }, + { type: 'text-plaintext', index: 0, start: 52, end: 58, substring: ' value' }, + { type: 'text-end', index: 0, start: 58, end: 58, substring: '' }, + { type: 'end-tag-open', index: 0, start: 58, end: 60, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('textarea elements with strict interpolation work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'textarea' }, + { type: 'start-tag-close', index: 0, start: 9, end: 10, substring: '>' }, + { type: 'bound-text-value', index: 0, start: 10, end: 10, substring: '' }, + { type: 'end-tag-open', index: 1, start: 0, end: 2, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('custom elements work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 8, substring: 'foo-bar' }, + { type: 'start-tag-close', index: 0, start: 8, end: 9, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 9, end: 11, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); + + it('template elements work', () => { + const expectedTokens = [ + { type: 'start-tag-open', index: 0, start: 0, end: 1, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 1, end: 9, substring: 'template' }, + { type: 'start-tag-close', index: 0, start: 9, end: 10, substring: '>' }, + { type: 'start-tag-open', index: 0, start: 10, end: 11, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 11, end: 14, substring: 'div' }, + { type: 'start-tag-close', index: 0, start: 14, end: 15, substring: '>' }, + { type: 'start-tag-open', index: 0, start: 15, end: 16, substring: '<' }, + { type: 'start-tag-name', index: 0, start: 16, end: 17, substring: 'p' }, + { type: 'start-tag-close', index: 0, start: 17, end: 18, substring: '>' }, + { type: 'end-tag-open', index: 0, start: 18, end: 20, substring: '' }, + { type: 'end-tag-open', index: 0, start: 22, end: 24, substring: '' }, + { type: 'end-tag-open', index: 0, start: 28, end: 30, substring: '' }, + ]; + const tokens = html``; + assert(deepEqual(tokens, expectedTokens), stringifyTokens(tokens)); + }); +}); + +describe('errors', () => { + it('throws when markup after end tag cannot be parsed', () => { + const callback = () => htmlol`
<`; + const expectedMessage = '[#110]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when attempting non-trivial interpolation of a textarea tag (preceding space)', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when attempting non-trivial interpolation of a textarea tag (succeeding space)', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when attempting non-trivial interpolation of a textarea tag', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when attempting non-trivial interpolation of a textarea tag via nesting', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws when interpolation of a textarea is at the end of the template', () => { + const callback = () => html``; + const expectedMessage = '[#155]'; + assertThrows(callback, expectedMessage, { startsWith: true }); + }); + + it('throws for declarative shadow root / shadowrootmode', () => { + const callback = () => htmlol`