Skip to content

Commit

Permalink
Simplify x-element.
Browse files Browse the repository at this point in the history
This closes #57, closes #52, closes #46, closes #42, closes #36,
closes #31, closes #30, closes #28, closes #26, and closes #25.
  • Loading branch information
theengineear committed Jun 2, 2020
1 parent 77a31c5 commit 509c148
Show file tree
Hide file tree
Showing 12 changed files with 379 additions and 74 deletions.
3 changes: 2 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { test } from '../../../@netflix/x-test/x-test.js';

test('./test-analysis-errors.html');
test('./test-runtime-errors.html');
test('./test-connected-errors.html');
test('./test-attribute-changed-errors.html');
test('./test-upgrade.html');
test('./test-render-root.html');
test('./test-listeners.html');
Expand Down
16 changes: 16 additions & 0 deletions test/test-analysis-errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ it('attributes cannot be duplicated', () => {
assert(passed, 'appropriate error message is thrown');
});

it('value must be a simple value or a function', () => {
let passed = false;
try {
class TestElement extends XElement {
static get properties() {
return { badValue: { value: {} } };
}
}
customElements.define('test-element', TestElement);
} catch (error) {
const expected = 'Unexpected value for "TestElement.properties.badValue.attribute" (expected Boolean, String, Number, or Function).';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('reflect should be a boolean', () => {
let passed = false;
try {
Expand Down
1 change: 0 additions & 1 deletion test/test-attr-binding.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import XElement from '../x-element.js';
import { assert, it } from '../../../@netflix/x-test/x-test.js';

// TODO: test that error is thrown when setting an internal property.
class TestElement extends XElement {
static get properties() {
return {
Expand Down
8 changes: 8 additions & 0 deletions test/test-attribute-changed-errors.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!doctype html>
<html>
<body>
<meta charset="UTF-8">
<script type="module" src="test-attribute-changed-errors.js"></script>
<h3>View Console</h3>
</body>
</html>
101 changes: 101 additions & 0 deletions test/test-attribute-changed-errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import XElement from '../x-element.js';
import { assert, it } from '../../../@netflix/x-test/x-test.js';

it('errors are thrown on attribute changed for setting values with bad types', () => {
// We cannot try-catch setAttribute, so we fake the attributeChangedCallback.
class TestElement extends XElement {
static get properties() {
return {
object: {
type: Object,
},
};
}
}
customElements.define('test-element-1', TestElement);
const el = new TestElement();
el.connectedCallback();
let passed = false;
try {
el.attributeChangedCallback('object', null, '{}');
} catch (error) {
const expected = 'Unexpected deserialization for "TestElement.properties.object" (cannot deserialize into Object).';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('errors are thrown on attribute change for read-only properties', () => {
// We cannot try-catch setAttribute, so we fake the attributeChangedCallback.
class TestElement extends XElement {
static get properties() {
return {
readOnly: {
type: String,
readOnly: true,
},
};
}
}
customElements.define('test-element-2', TestElement);
const el = new TestElement();
let passed = false;
el.connectedCallback();
try {
el.attributeChangedCallback('read-only', null, 'nope');
} catch (error) {
const expected = 'Property "TestElement.properties.readOnly" is readOnly (element authors may use "internalProperties").';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('errors are thrown on attribute change for internal properties', () => {
// We cannot try-catch setAttribute, so we fake the attributeChangedCallback.
class TestElement extends XElement {
static get properties() {
return {
internal: {
type: String,
internal: true,
},
};
}
}
customElements.define('test-element-3', TestElement);
const el = new TestElement();
el.connectedCallback();
let passed = false;
try {
el.attributeChangedCallback('internal', null, 'nope');
} catch (error) {
const expected = 'Property "TestElement.properties.internal" is internal (element authors may use "internalProperties").';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('errors are thrown on attribute change for resolved properties', () => {
// We cannot try-catch setAttribute, so we fake the attributeChangedCallback.
class TestElement extends XElement {
static get properties() {
return {
resolved: {
type: String,
resolver: () => {},
},
};
}
}
customElements.define('test-element-4', TestElement);
const el = new TestElement();
el.connectedCallback();
let passed = false;
try {
el.attributeChangedCallback('resolved', null, 'nope');
} catch (error) {
const expected = 'Property "TestElement.properties.resolved" has resolver (cannot be set).';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<body>
<meta charset="UTF-8">
<script type="module" src="test-runtime-errors.js"></script>
<script type="module" src="test-connected-errors.js"></script>
<h3>View Console</h3>
</body>
</html>
File renamed without changes.
30 changes: 29 additions & 1 deletion test/test-internal-properties.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import XElement from '../x-element.js';
import { assert, it } from '../../../@netflix/x-test/x-test.js';

// TODO: test that we cannot set these on normal propertiesProxy.
class TestElement extends XElement {
static get properties() {
return {
Expand Down Expand Up @@ -75,3 +74,32 @@ it('cannot be written to from "internalProperties" if resolved', async () => {
}
assert(passed, 'throws appropriate error event');
});

it('cannot set to known properties', () => {
class BadTestElement extends XElement {
static get properties() {
return {
internalProperty: {
type: String,
internal: true,
},
};
}
static template(html) {
return properties => {
properties.internalProperty = 'Dromedary';
return html`<div>${properties.internalProperty}</div>`;
};
}
}
customElements.define('bad-test-element-1', BadTestElement);
const el = new BadTestElement();
let passed = false;
try {
el.connectedCallback();
} catch (error) {
const expected = 'Cannot set "BadTestElement.properties.internalProperty" via "properties".';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});
102 changes: 83 additions & 19 deletions test/test-normal-properties.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import XElement from '../x-element.js';
import { assert, it } from '../../../@netflix/x-test/x-test.js';

// TODO: test that we cannot set these on normal propertiesProxy.
class TestElement extends XElement {
static get properties() {
return {
Expand All @@ -19,24 +18,6 @@ class TestElement extends XElement {
}
customElements.define('test-element', TestElement);

class TestElementBadWrite extends XElement {
static get properties() {
return {
normalProperty: {
type: String,
},
};
}
static template(html) {
return properties => {
properties.normalProperty = 'Dromedary';
return html`<div>${properties.normalProperty}</div>`;
};
}
}
customElements.define('test-element-bad-write', TestElementBadWrite);


it('initialization', async () => {
const el = document.createElement('test-element');
document.body.appendChild(el);
Expand All @@ -55,3 +36,86 @@ it('can be read', async () => {
'text content was updated',
);
});

it('cannot set to known properties', () => {
class BadTestElement extends XElement {
static get properties() {
return {
normalProperty: {
type: String,
},
};
}
static template(html) {
return properties => {
properties.normalProperty = 'Dromedary';
return html`<div>${properties.normalProperty}</div>`;
};
}
}
customElements.define('bad-test-element-1', BadTestElement);
const el = new BadTestElement();
let passed = false;
try {
el.connectedCallback();
} catch (error) {
const expected = 'Cannot set "BadTestElement.properties.normalProperty" via "properties".';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('cannot set to unknown properties', () => {
class BadTestElement extends XElement {
static get properties() {
return {
normalProperty: {
type: String,
},
};
}
static template(html) {
return properties => {
properties.doesNotExist = 'Dromedary';
return html`<div>${properties.normalProperty}</div>`;
};
}
}
customElements.define('bad-test-element-2', BadTestElement);
const el = new BadTestElement();
let passed = false;
try {
el.connectedCallback();
} catch (error) {
const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});

it('cannot get unknown properties', () => {
class BadTestElement extends XElement {
static get properties() {
return {
normalProperty: {
type: String,
},
};
}
static template(html) {
return ({ doesNotExist }) => {
return html`<div>${doesNotExist}</div>`;
};
}
}
customElements.define('bad-test-element-3', BadTestElement);
const el = new BadTestElement();
let passed = false;
try {
el.connectedCallback();
} catch (error) {
const expected = 'Property "BadTestElement.properties.doesNotExist" does not exist.';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});
30 changes: 29 additions & 1 deletion test/test-read-only-properties.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import XElement from '../x-element.js';
import { assert, it } from '../../../@netflix/x-test/x-test.js';

// TODO: test that we cannot set these on normal propertiesProxy.
class TestElement extends XElement {
static get properties() {
return {
Expand Down Expand Up @@ -59,3 +58,32 @@ it('cannot be read from "internalProperties"', () => {
}
assert(passed, 'appropriate error is thrown');
});

it('cannot set to known properties', () => {
class BadTestElement extends XElement {
static get properties() {
return {
readOnlyProperty: {
type: String,
readOnly: true,
},
};
}
static template(html) {
return properties => {
properties.readOnlyProperty = 'Dromedary';
return html`<div>${properties.readOnlyProperty}</div>`;
};
}
}
customElements.define('bad-test-element-1', BadTestElement);
const el = new BadTestElement();
let passed = false;
try {
el.connectedCallback();
} catch (error) {
const expected = 'Cannot set "BadTestElement.properties.readOnlyProperty" via "properties".';
passed = error.message === expected;
}
assert(passed, 'appropriate error message is thrown');
});
Loading

0 comments on commit 509c148

Please sign in to comment.