Skip to content

Commit

Permalink
refine behavior, tests, and error conditions for create and compose o…
Browse files Browse the repository at this point in the history
…f components
  • Loading branch information
martypdx committed Mar 30, 2024
1 parent e16254a commit d12df95
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 63 deletions.
3 changes: 1 addition & 2 deletions packages/chronos/channels/branch.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ beforeEach(context => {

describe('promise', () => {

test.only('...transforms', async ({ fixture, find, expect, childHTML }) => {
test('...transforms', async ({ fixture, find, expect, childHTML }) => {
const promise = Promise.resolve(['felix', 'duchess', 'stimpy']);
const [Count, List, Map] = branch(
promise,
Expand All @@ -42,7 +42,6 @@ describe('promise', () => {
"<p><!--0--></p>",
"<p><!--0--></p>",
<!--3-->,
<!--1-->,
]
`);
});
Expand Down
5 changes: 2 additions & 3 deletions packages/chronos/channels/channel.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ describe('promise', () => {

await find('felix', { exact: false });
expect(fixture.innerHTML).toMatchInlineSnapshot(
`"<p>felix<!--1--></p><p>duchess<!--1--></p><p>garfield<!--1--></p><!--3--><!--1-->"`
`"<p>felix<!--1--></p><p>duchess<!--1--></p><p>garfield<!--1--></p><!--3-->"`
);
});

test.only('transform, { start, init }', async ({ fixture, find, expect }) => {
test('transform, { start, init }', async ({ fixture, find, expect }) => {
const promise = sleep(50).then(() => 'duchess');

const Cat = channel(promise, name => {
Expand All @@ -59,7 +59,6 @@ describe('promise', () => {


const cat = <Cat />;
console.log('*******', cat.childNodes.length, cat.childNodes[0].nodeType);
fixture.append(cat);
expect(fixture.innerHTML).toMatchInlineSnapshot(`"choose wisely<!--1-->"`);

Expand Down
97 changes: 51 additions & 46 deletions packages/maya/compose/compose.component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const RenderObject = {
return runCompose(name, elementWithAnchor);
}
};
const CONSTRUCTORS = [Component, ClassComp, RenderObject, ArrowComp];
const CONSTRUCTORS = [Component, ClassComp, ArrowComp, RenderObject];

function ComponentP({ name }) {
return Promise.resolve(runCompose(name, elementWithAnchor));
Expand All @@ -52,47 +52,22 @@ describe('create element', () => {
test('pin .prototype.constructor for function, arrow fn, class', ({ expect }) => {
expect(Component.prototype.constructor).toBeDefined();
expect(ClassComp.prototype.constructor).toBeDefined();
expect(ClassComp.prototype.constructor).toBeDefined();
expect(ArrowComp.prototype?.constructor).not.toBeDefined();
expect(RenderObject.prototype?.constructor).not.toBeDefined();
});

describe.each(CONSTRUCTORS)('%o', Constructor => {
const expected = `<div>felix<!--1--></div>`;
test('prop-agation', ({ expect }) => {
const dom = createComponent(Constructor, { name: 'felix' });
expect(dom.outerHTML).toBe(expected);
});
});

describe.each(ASYNC_CONSTRUCTORS)('%o', Constructor => {
const expected = `<div>felix<!--1--></div><!--1-->`;
test('prop-agation', async ({ expect, fixture, find }) => {
const dom = createComponent(Constructor, { name: 'felix' });
fixture.append(dom);
await find('felix');
expect(fixture.innerHTML).toBe(expected);
});
});

describe.each(CONSTRUCTORS.concat(ASYNC_CONSTRUCTORS))('promised %o', Constructor => {
const expected = `<div>felix<!--1--></div><!--1-->`;
test('prop-agation', async ({ expect, fixture, find }) => {
const dom = createComponent(Promise.resolve(Constructor), { name: 'felix' });
fixture.append(dom);
await find('felix');
expect(fixture.innerHTML).toBe(expected);
});
});

describe.each(CONSTRUCTORS.concat(ASYNC_CONSTRUCTORS))('promised %o', Constructor => {
const expected = `<div>felix<!--1--></div><!--1-->`;
test('prop-agation', async ({ expect, fixture, find }) => {
const dom = createComponent(Promise.resolve(Constructor), { name: 'felix' });
fixture.append(dom);
await find('felix');
expect(fixture.innerHTML).toBe(expected);
});
const expected = `<div>felix<!--1--></div>`;
const create = Constructor => createComponent(Constructor, { name: 'felix' });
test('constructed values', async ({ expect }) => {
expect(create(Component).outerHTML).toBe(expected);
expect(create(ArrowComp).outerHTML).toBe(expected);
expect(create(RenderObject).outerHTML).toBe(expected);
expect(create(ClassComp).render().outerHTML).toBe(expected);

expect((await create(ComponentP)).outerHTML).toBe(expected);
expect((await create(RenderObject)).outerHTML).toBe(expected);
expect((await create(ArrowCompP)).outerHTML).toBe(expected);
expect((await create(ClassCompP).render())().outerHTML).toBe(expected);
});

});
Expand Down Expand Up @@ -142,12 +117,12 @@ describe('prop-agation', () => {
test('Non-render class is error', async ({ expect }) => {
class MyClass { }
expect(() => {
createComponent(MyClass, { name: 'felix' });
composeComponent(null, [MyClass]);
}).toThrowErrorMatchingInlineSnapshot(
`
[TypeError: Invalid compose {...} input type "object", value [object Object].
Did you forget to return a value from "MyClass"?
Did you forget to return a value from "MyClass"if a function, or a "render" method if a class?
Received as:
Expand All @@ -171,7 +146,7 @@ describe('compose element', () => {
});
});

describe.only.each(CONSTRUCTORS.concat(ASYNC_CONSTRUCTORS))('Promised %o', Constructor => {
describe.each(CONSTRUCTORS.concat(ASYNC_CONSTRUCTORS))('Promised %o', Constructor => {
const expected = `<div><div>felix<!--1--></div><!--1--></div>`;
test('prop-agation', async ({ expect, fixture, find }) => {
const { dom, anchor } = elementWithAnchor();
Expand Down Expand Up @@ -203,21 +178,51 @@ describe('compose element', () => {
});

describe('SyncAsync from', () => {
test('initial render', async ({ expect, fixture, find }) => {
const syncWrapper = SyncAsync.from('cat coming', ClassCompP);
test('basic render', async ({ expect, fixture, find }) => {
const syncWrapper = SyncAsync.from('sync cat', Promise.resolve('async cat'));
const dom = createComponent(syncWrapper);
expect(dom).toMatchInlineSnapshot(`
<DocumentFragment>
sync cat
<!--1-->
</DocumentFragment>
`);

fixture.append(dom);
expect(fixture.innerHTML).toMatchInlineSnapshot(`"sync cat<!--1-->"`);

await find('async cat');
expect(fixture.innerHTML).toBe(`async cat<!--1-->`);
});

class Loading {
constructor({ name }) {
this.name = name;
}
render() {
return `Loading ${this.name}`;
}
}

test('creates', async ({ expect, fixture, find }) => {
const syncWrapper = SyncAsync.from(Loading, Promise.resolve(ClassComp));
const dom = createComponent(syncWrapper, { name: 'felix' });
expect(dom).toMatchInlineSnapshot(`
<DocumentFragment>
cat coming
Loading felix
<!--1-->
</DocumentFragment>
`);

fixture.append(dom);
expect(fixture.innerHTML).toMatchInlineSnapshot(`"cat coming<!--1-->"`);
expect(fixture.innerHTML).toMatchInlineSnapshot(
`"Loading felix<!--1-->"`
);

await find('felix');
expect(fixture.innerHTML).toBe(`<div>felix<!--1--></div><!--1-->`);
expect(fixture.innerHTML).toMatchInlineSnapshot(
`"<div>felix<!--1--></div><!--1-->"`
);
});
});

19 changes: 14 additions & 5 deletions packages/maya/compose/compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export function compose(anchor, input, keepLast, props, slottable) {
const isRenderObject = obj => obj && typeof obj === 'object' && obj.render && typeof obj.render === 'function';

export function composeComponent(anchor, [Constructor, props, slottable]) {
createCompose(Constructor, props, slottable, anchor);
}

export function createCompose(Constructor, props, slottable, anchor) {
const out = create(Constructor, props, slottable, anchor);
if(out !== anchor) compose(anchor, out);
}
Expand Down Expand Up @@ -146,18 +150,19 @@ function create(input, props, slottable, anchor) {
}

if(input instanceof SyncAsync) {
compose(anchor, input.sync, true, props, slottable);
compose(anchor, input.async, false, props, slottable);
createCompose(input.sync, props, slottable, anchor);
createCompose(input.async, props, slottable, anchor);
}
else if(input[Symbol.asyncIterator]) {
composeAsyncIterator(anchor, input, false, props, slottable);
}
else if(input instanceof Promise) {
input.then(value => {
compose(anchor, create(value, props, slottable), true, null, null);
createCompose(value, props, slottable, anchor);
});
}
else if(Array.isArray(input)) {
// TODO: map to createCompose
composeArray(anchor, input, false);
}
else {
Expand Down Expand Up @@ -238,8 +243,12 @@ function throwTypeErrorForObject(obj) {
let message = '';
try {
const fnName = obj.constructor?.name;
if(fnName && fnName !== 'Object') {
message += `\n\nDid you forget to return a value from "${fnName}"?`;
if(fnName === 'Object') {
message += `\n\nDid you mean to include a "render" method?`;
}
else if(fnName) {
message += `\n\nDid you forget to return a value from "${fnName}"\
if a function, or a "render" method if a class?`;
}
const json = JSON.stringify(obj, null, 2);
message += `\n\nReceived as:\n\n${json}\n\n`;
Expand Down
16 changes: 9 additions & 7 deletions packages/maya/compose/compose.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,18 @@ describe('throws on invalid types', () => {
expect(() => {
compose(null, { name: 'felix' });
}).toThrowErrorMatchingInlineSnapshot(`
[TypeError: Invalid compose {...} input type "object", value [object Object].
[TypeError: Invalid compose {...} input type "object", value [object Object].
Received as:
Did you mean to include a "render" method?
{
"name": "felix"
}
Received as:
]
`);
{
"name": "felix"
}
]
`);
});

test('throw on Symbol', ({ expect }) => {
Expand Down

0 comments on commit d12df95

Please sign in to comment.