@v4fire/client
Version:
V4Fire client core library
450 lines (363 loc) • 9.72 kB
JavaScript
// @ts-check
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
/**
* @typedef {import('playwright').Page} Page
*/
const
h = include('tests/helpers').default;
/**
* Starts a test
*
* @param {Page} page
* @returns {!Promise<void>}
*/
module.exports = (page) => {
const defaultItems = [
{value: 'bar'},
{
value: 'foo',
children: [
{value: 'foo_1'},
{value: 'foo_2'},
{
value: 'foo_3',
children: [{value: 'foo_3_1'}]
},
{value: 'foo_4'},
{value: 'foo_5'},
{value: 'foo_6'}
]
}
];
beforeEach(async () => {
await page.evaluate(() => {
globalThis.removeCreatedComponents();
});
});
describe('b-tree rendering data from the `items` prop', () => {
it('initialization', async () => {
const
target = await init();
await expectAsync(target.evaluate((ctx) => ctx.isFunctional === false))
.toBeResolvedTo(true);
await waitForItemsRender(9);
const
promises = await Promise.all(checkOptionTree({items: defaultItems, target})),
checkboxes = await page.$$('.b-checkbox');
expect(promises.length).toEqual(checkboxes.length);
});
it('all items unfolded by default', async () => {
const items = [
{value: 'bar'},
{
value: 'foo',
children: [
{value: 'foo_1'},
{value: 'foo_2'},
{
value: 'foo_3',
children: [{value: 'foo_3_1', children: [{value: 'foo_3_1_1'}]}]
}
]
}
];
const target = await init({items, folded: false});
await Promise.all(checkOptionTree({items, target}));
});
describe('setting of the external `renderFilter`', () => {
it('renders by one with a timeout', async () => {
const bTree = await init({attrs: {
renderChunks: 1,
renderFilter: 'return () => new Promise((res) => setTimeout(() => res(true), 0.5.second()))'
}});
await h.bom.waitForIdleCallback(page);
const
waitForRender = () => bTree.evaluate((ctx) => ctx.async.sleep(500)),
getCheckboxes = () => page.$$('.b-checkbox');
await waitForRender();
await expect((await getCheckboxes()).length).toBe(1);
await waitForRender();
await expect((await getCheckboxes()).length).toBe(2);
await waitForRender();
await expect((await getCheckboxes()).length).toBe(3);
});
it('renders using the context data', async () => {
await init({
attrs: {renderFilter: `return (ctx, item) => {
if (ctx.level === 0 ) {
return true;
}
return false;
}`}
});
await h.bom.waitForIdleCallback(page);
await expect((await page.$$('.b-checkbox')).length).toBe(2);
});
});
it('setting of the external `nestedRenderFilter`', async () => {
const items = [
{
value: 'foo',
children: [
{value: 'foo_1'},
{value: 'foo_2'},
{
value: 'foo_3',
children: [{value: 'foo_3_1', children: [{value: 'foo_3_1_1'}]}]
}
]
},
{value: 'bar'}
];
await init({
items,
attrs: {
renderChunks: 1,
nestedRenderFilter: 'return () => new Promise((res) => setTimeout(() => res(true), 0.5.second()))'
}
});
await waitForItemsRender(2);
await waitForItemsRender(3);
await waitForItemsRender(4);
await waitForItemsRender(5);
await waitForItemsRender(6);
await waitForItemsRender(7);
});
async function init({items, attrs, content} = {}) {
if (items == null) {
items = defaultItems;
}
await page.evaluate(({items, attrs, content}) => {
globalThis.removeCreatedComponents();
parseParams(content);
parseParams(attrs);
const baseAttrs = {
theme: 'demo',
item: 'b-checkbox-functional',
items,
id: 'target',
renderChunks: 2
};
const scheme = [
{
attrs: {
...baseAttrs,
...attrs
},
content
}
];
globalThis.renderComponents('b-tree', scheme);
function parseParams(obj) {
Object.forEach(obj, (el, key) => {
// eslint-disable-next-line no-new-func
obj[key] = /return /.test(el) ? Function(el)() : el;
});
}
}, {items, attrs, content});
await h.component.waitForComponentStatus(page, '#target', 'ready');
return h.component.waitForComponent(page, '#target');
}
});
describe('b-tree rendering data from a data provider', () => {
it('initialization', async () => {
await init();
await waitForItemsRender(14);
await h.bom.waitForIdleCallback(page);
expect((await page.$$('.b-checkbox')).length).toBe(14);
});
async function init() {
await page.evaluate(() => {
globalThis.removeCreatedComponents();
const scheme = [
{
attrs: {
theme: 'demo',
dataProvider: 'demo.NestedList',
item: 'b-checkbox-functional',
id: 'target'
}
}
];
globalThis.renderComponents('b-tree', scheme);
});
await h.component.waitForComponentStatus(page, '.b-tree', 'ready');
return h.component.waitForComponent(page, '#target');
}
});
describe('b-tree providing of the `default` slot', () => {
it('initialization', async () => {
const
target = await init();
await expectAsync(target.evaluate((ctx) => ctx.isFunctional === false))
.toBeResolvedTo(true);
await waitForItemsRender(9, '[data-test-ref]');
const
promises = await Promise.all(checkOptionTree({items: defaultItems, target})),
refs = await h.dom.getRefs(page, 'item');
expect(promises.length).toEqual(refs.length);
});
async function init() {
await page.evaluate((items) => {
const defaultSlot = {
tag: 'div',
content: 'Item',
attrs: {
'data-test-ref': 'item'
}
};
const scheme = [
{
attrs: {
items,
id: 'target',
theme: 'demo'
},
content: {
default: defaultSlot
}
}
];
globalThis.renderComponents('b-tree', scheme);
}, defaultItems);
await h.component.waitForComponentStatus(page, '#target', 'ready');
return h.component.waitForComponent(page, '#target');
}
});
describe('items changed.', () => {
it('default', async () => {
const
target = await init();
expect(
await target.evaluate(async (ctx) => {
const
log = [];
ctx.on('onItemsChange', (val) => {
log.push(val);
});
ctx.items = [{label: 'Bar', value: 1}];
log.push(ctx.items);
await ctx.localEmitter.promisifyOnce('asyncRenderComplete');
return log;
})
).toEqual([
[{label: 'Bar', value: 1}],
[{label: 'Bar', value: 1}]
]);
});
it('folded false', async () => {
const
target = await init({folded: false});
expect(
await target.evaluate(async (ctx) => {
ctx.items = [
{value: 1},
{
value: 2,
children: [{value: 4}]
},
{
value: 3,
children: [{value: 5}]
}
];
await ctx.localEmitter.promisifyOnce('asyncRenderComplete');
return [ctx.getFoldedMod(2), ctx.getFoldedMod(3)];
})
).toEqual(['false', 'false']);
});
it('node is folded after change', async () => {
const
target = await init();
expect(
await target.evaluate(async (ctx) => {
await ctx.unfold('foo');
ctx.items = [
{value: 0},
{
value: 1,
children: [{value: 3}]
},
{
value: 2,
children: [{value: 4}]
}
];
await ctx.localEmitter.promisifyOnce('asyncRenderComplete');
return ctx.getFoldedMod(2);
})
).toBe('true');
});
async function init(attrs) {
await page.evaluate(([items, attrs]) => {
const scheme = [
{
attrs: {
items,
id: 'target',
theme: 'demo',
...attrs
}
}
];
globalThis.renderComponents('b-tree', scheme);
}, [defaultItems, attrs]);
return h.component.waitForComponent(page, '#target');
}
});
async function waitForItemsRender(v, selector = '.b-checkbox') {
await page.waitForFunction(([v, selector]) => document.querySelectorAll(selector).length === v, [v, selector]);
await expect((await page.$$(selector)).length).toBe(v);
}
function getFoldedClass(target, value = true) {
return target.evaluate(
(ctx, v) => ctx.block.getFullElName('node', 'folded', v),
value
);
}
function checkOptionTree({items, target, queue = [], level = 0, foldSelector}) {
items.forEach((item) => {
const
isBranch = Object.isArray(item.children);
queue.push((async () => {
const
id = await target.evaluate((ctx, value) => ctx.valueIndexes.get(value), item.value),
element = await page.waitForSelector(`[data-id="${id}"]`, {state: 'attached'});
await expectAsync(
element.getAttribute('data-level')
).toBeResolvedTo(String(level));
const
foldedPropValue = await target.evaluate((ctx) => ctx.folded),
foldedInitModValue = item.folded != null ? item.folded : foldedPropValue,
foldedClass = await getFoldedClass(target, foldedInitModValue);
if (isBranch) {
const
selector = foldSelector || await target.evaluate((ctx) => `.${ctx.block.getFullElName('fold')}`),
fold = await page.waitForSelector(selector);
await expectAsync(
element.getAttribute('class').then((className) => className.includes(foldedClass))
).toBeResolvedTo(true);
if (foldedInitModValue) {
await fold.click();
}
}
})());
if (isBranch) {
checkOptionTree({
items: item.children,
level: level + 1,
target,
queue,
foldSelector
});
}
});
return queue;
}
};