diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js index bf1aeac..300486e 100644 --- a/src/mocks/handlers.js +++ b/src/mocks/handlers.js @@ -2,20 +2,21 @@ import { rest } from 'msw' import pokemonsMock from "../../stories/pokemons.mock"; +const resolveAsPokemons = (res,ctx) => res(ctx.json(pokemonsMock)); + export const handlers = [ rest.get("*/api/v2/pokemon", (req, res, ctx) => { return res(ctx.json(pokemonsMock)); }), rest.get("*/noreturn", (req, res, ctx) => - { return new Promise((resolve)=>{ setTimeout(()=> - { resolve(res(ctx.json(pokemonsMock))) - }, 10000)}); // 1 second to be able to catch the initial state before the full data returned; + { return new Promise((resolve)=>{ setTimeout(()=> resolve( resolveAsPokemons(res,ctx) ), 10000)}); + // 10 seconds to be able to catch the initial state before the full data returned; }), rest.get("*/reflect", (req, res, ctx) => { const headersMap = {}; [...req.headers.entries()].map( ([key,val]) => headersMap[key] = val ); ctx.set(headersMap); - return res(ctx.json(pokemonsMock)); + return resolveAsPokemons(res,ctx); }) ]; diff --git a/stories/data-slices.stories.js b/stories/data-slices.stories.js new file mode 100644 index 0000000..8aea5f7 --- /dev/null +++ b/stories/data-slices.stories.js @@ -0,0 +1,129 @@ +import '../src/custom-element.js'; + +export default +{ title: 'Events and data Slice', component: 'custom-element', argTypes: + { title: { control: 'text', defaultValue: 'simple aDceTemplate' } + , description: { control: 'text', defaultValue: '' } + , aDceTemplate: { control: 'text', defaultValue: `Hi` } + } +}; + +function Template( { title, description, aDceTemplate, tag, dce } ) +{ + return ` +
+ `; +} + +function demo( title, description, aDceTemplate, tag='', dce='' ) +{ const ret = Template.bind( {} ); + ret.args = { title, description, aDceTemplate,tag,dce} + return ret; +} +export const SliceInitializationChangeOnEvent = demo( `A. slice initialization, change on event`, +'initial value should be 0; + and - should change the number in input field', +` + + +{//clickcount}
+`);
+
+export const EventWithinSlice = demo( `B. event within slice`,
+'move the mouse over TEXTAREA and click to see slice and slice event changed',
+`
+{//slice/s/event/@offsetY}
{//slice/typed}
+`);
+
+export const InitDefaultEvent = demo( `2. initial slice value, slice change on event. slice⮂value, w/ initial`,
+'initial value from input; type and unfocus to see slice changed',
+`
+ //slice/s : {//slice/s}
+`);
+
+export const InitAndKeyUp = demo( `3. initial slice value, slice change on event. slice⮂value, w/ initial`,
+'initial value from input; type to see slice changed',
+`
+ //slice/s : {//slice/s}
+`);
+
+export const InitByAttribute = demo( `4. initial slice value from attribute, slice change on event.`,
+'initial value from input; type to see slice changed',
+`
+{//slice/s}
+`,'dce-1','{//slice/s}
+`);
+
+export const InitByInputValueByButton = demo( `6. initial slice value from input, button ignored till change on click.`,
+'initial value from input as \'anonymous\'; on button click change to \'broccoli\'',
+`
+
+
+{//nickname}
+`);
+
+export const InitBySliceElement = demo( `7. initial slice value from SLICE element, button ignored till change on click.`,
+'synthetic SLICE element serves as initial value holder',
+`
+
+//clickcount : {//clickcount}
+`);
+
+export const InitByFewSliceElement = demo( `8. multiple slices by SLICE element, button ignored till change on click.`,
+'few synthetic SLICE elements serve as initial value holder',
+`
+
+//clicked : {//clicked}
+//focused : {//focused}
+`);
+
+export const SlicePointsAttribute = demo( `9. slice in attribute`,
+'initial attribute value should be smile as emoji and :) on blur from input it should be updated from value',
+`
+
+
+Type and unfocus to update emotion attribute: {emotion}
+`,'emotional-element', `
+
+
+`);
diff --git a/stories/dom-merge.stories.js b/stories/dom-merge.stories.js
index e0e583e..2a11974 100644
--- a/stories/dom-merge.stories.js
+++ b/stories/dom-merge.stories.js
@@ -41,7 +41,7 @@ TextareaOnBlur.args =
export const InputOnChange = Template.bind( {} );
InputOnChange.args =
{ title: 'input value, type and observe char count update on keyup'
-, slot: `
+, slot: `
Character count:
{string-length(//slice/txt)}
diff --git a/stories/http-request.stories.js b/stories/http-request.stories.js
index 0fc1f06..b38d330 100644
--- a/stories/http-request.stories.js
+++ b/stories/http-request.stories.js
@@ -25,10 +25,11 @@ function Template( { title, tag , slice, url } )
url="${url}"
slice="${slice}"
>
-
+ Pokemon Count: {count(/datadom/slice/${slice}//results)}
+
loading...
-
+
@@ -66,7 +67,7 @@ LifecycleInitialized = ()=>`
type="text"
>
Content of //slice/request_slice
before response available
-
+
@@ -106,14 +107,14 @@ from ${url}
Samples
-//slice/request_slice/request/headers/@mode
-
-//slice/request_slice/response/headers/@content-type
-
-//slice/request_slice/response/@status
-
+//slice/request_slice/value/request/headers/@mode
+
+//slice/request_slice/value/response/headers/@content-type
+
+//slice/request_slice/value/response/@status
+
-
+
diff --git a/stories/local-storage.stories.js b/stories/local-storage.stories.js
index 7a00de3..288a464 100644
--- a/stories/local-storage.stories.js
+++ b/stories/local-storage.stories.js
@@ -50,7 +50,7 @@ LocalStorageLive.args =
value: '{"cherries": 12, "lemons":1 }',
tag: 'ls-live-component',
body: `
-
+
@@ -59,7 +59,7 @@ LocalStorageLive.args =
🤔
-
+
`
diff --git a/stories/location-element.stories.js b/stories/location-element.stories.js
index 463a916..ed74a4d 100644
--- a/stories/location-element.stories.js
+++ b/stories/location-element.stories.js
@@ -36,7 +36,7 @@ function Template( { title, tag, src, slice, live } )
-
+
@@ -46,7 +46,7 @@ function Template( { title, tag, src, slice, live } )
params
-
+
diff --git a/stories/parameters.stories.js b/stories/parameters.stories.js
index 6b6dc63..417fadd 100644
--- a/stories/parameters.stories.js
+++ b/stories/parameters.stories.js
@@ -65,7 +65,7 @@ function TemplateSlice( { title } )
-
+
title attribute: {$title}
diff --git a/test/data-slices-mouse.test.js b/test/data-slices-mouse.test.js
new file mode 100644
index 0000000..a76c2ec
--- /dev/null
+++ b/test/data-slices-mouse.test.js
@@ -0,0 +1,50 @@
+import {fixture, expect, aTimeout, oneEvent} from '@open-wc/testing';
+
+import '../src/custom-element.js';
+import defaults, {
+ EventWithinSlice,
+ SliceInitializationChangeOnEvent
+} from "../stories/data-slices.stories";
+
+const defs = {};
+Object.keys(defaults.argTypes).map(k => defs[k] = defaults.argTypes[k].defaultValue);
+const renderStory = async (story) => fixture(story({...defs, ...story.args}));
+
+const $ = (css, node) => node.querySelector(css);
+
+describe('mouse events', () =>
+{
+ it('B. event within slice', async () =>
+ { // works only in manual mode with breakpoint after render
+ const el = await renderStory(EventWithinSlice);
+ const X = $('var',el);
+ const Y = $('code',el);
+ const input = $('textarea',el);
+ const validateValue = async (x,y)=>
+ { await aTimeout(20);
+ expect(X.innerText).to.equal(x);
+ // if( Y.innerText !== y){debugger}
+ // expect(Y.innerText).to.equal(y);
+ };
+
+ await validateValue('','')
+ const emitXy = ( x, y, eventName ) =>
+ { const ev = new MouseEvent(eventName,
+ {
+ screenX: x,
+ screenY: y,
+ clientX: x,
+ clientY: y,
+ offsetX: x,
+ offsetY: y,
+ }) ;
+
+ input.dispatchEvent( ev );
+ };
+
+ emitXy(10,20,'click');
+ await validateValue('x:10','20');
+ emitXy(30,40,'click');
+ await validateValue('x:30','40');
+ });
+});
diff --git a/test/data-slices.test.js b/test/data-slices.test.js
new file mode 100644
index 0000000..fef7898
--- /dev/null
+++ b/test/data-slices.test.js
@@ -0,0 +1,234 @@
+import {fixture, expect, aTimeout, oneEvent} from '@open-wc/testing';
+
+import '../src/custom-element.js';
+import defaults, {
+ EventWithinSlice,
+ InitAndKeyUp,
+ InitByAttribute,
+ InitByAttributeChangeByExpression, InitByFewSliceElement, InitByInputValueByButton, InitBySliceElement,
+ InitDefaultEvent,
+ SliceChangeOnEvent,
+ SliceInitializationChangeOnEvent, SlicePointsAttribute
+} from "../stories/data-slices.stories";
+
+const defs = {};
+Object.keys(defaults.argTypes).map(k => defs[k] = defaults.argTypes[k].defaultValue);
+const renderStory = async (story) => fixture(story({...defs, ...story.args}));
+
+const $ = (css, node) => node.querySelector(css);
+
+describe('data slices and events', () =>
+{
+ it('A. slice initialization, change on event', async () =>
+ {
+ const el = await renderStory(SliceInitializationChangeOnEvent);
+ const code = $('code',el);
+ const input = $('input',el);
+ const validateValue = async (v)=>
+ { await aTimeout(20);
+ expect(code.innerText).to.equal(v);
+ expect(input.value).to.equal(v);
+ expect(input.getAttribute('value')).to.equal(v);
+ };
+ await validateValue('0')
+ $('button',el).click();
+ await validateValue('1')
+
+
+ $('button',el).click(); // 2
+ await validateValue('2')
+ $('button',el).click(); // 3
+ await validateValue('3')
+
+ $('button+button',el).click();
+ await validateValue('2')
+ $('button+button',el).click();
+ await validateValue('1')
+ $('button+button',el).click();
+ await validateValue('0')
+ $('button+button',el).click();
+ await validateValue('-1')
+ });
+
+ it('1. slice change on event. 1:1 slice⮂value', async () =>
+ {
+ const el = await renderStory(SliceChangeOnEvent);
+ const code = $('code', el);
+ const input = $('input',el);
+ const validateValue = async (v)=>
+ { await aTimeout(20);
+ expect(code.innerText).to.equal(v);
+ expect(input.value).to.equal(v);
+ // expect(input.getAttribute('value')).to.equal(v);
+ };
+ await validateValue('')
+ input.value="abc";
+ input.dispatchEvent(new Event('change'));
+ await validateValue('abc')
+ });
+
+ it('2. initial slice value, slice change on event. slice⮂value, w/ initial', async () =>
+ {
+ const el = await renderStory(InitDefaultEvent);
+ const code = $('code', el);
+ const input = $('input',el);
+ const validateValue = async (v)=>
+ { await aTimeout(20);
+ expect(code.innerText).to.equal(v);
+ expect(input.value).to.equal(v);
+ // expect(input.getAttribute('value')).to.equal(v);
+ };
+ await validateValue('A')
+ input.value="abc";
+ input.dispatchEvent(new Event('change'));
+ await validateValue('abc')
+ });
+
+ it('3. initial slice value, slice change on event. slice⮂value, w/ initial', async () =>
+ {
+ const el = await renderStory(InitAndKeyUp);
+ const code = $('code', el);
+ const input = $('input',el);
+ const validateValue = async (v)=>
+ { await aTimeout(20);
+ expect(code.innerText).to.equal(v);
+ expect(input.value).to.equal(v);
+ // expect(input.getAttribute('value')).to.equal(v);
+ };
+
+ expect(code.innerText).to.equal('');
+ expect(input.value).to.equal('B');
+ input.value="DE";
+ input.dispatchEvent(new Event('input'));
+ await validateValue('DE')
+ });
+
+ it('4. initial slice value from attribute, slice change on event.', async () =>
+ {
+ const el = await renderStory(InitByAttribute);
+ const d1 = $('#e1' , el )
+ , d2 = $('#e2' , el )
+ , i1 = $('#e1 input' , el )
+ , i2 = $('#e2 input' , el );
+
+
+ expect(d1.innerText).to.include('😁');
+ expect(d2.innerText).to.include('🤗');
+ expect(i1.value).to.equal('😁');
+ expect(i2.value).to.equal('🤗');
+ i1.value="AB";
+ i1.dispatchEvent(new Event('input'));
+ await aTimeout(20);
+ expect($('code',d1).innerText).to.equal('AB');
+ });
+
+ it('5. initial slice value from attribute, slice change on event.', async () =>
+ {
+ const el = await renderStory(InitByAttributeChangeByExpression);
+ const input = $('input' , el )
+ , code = $('code' , el );
+
+
+ expect(input.value).to.equal('B');
+ expect(code.innerText).to.equal('xB');
+
+ input.value="ABC";
+ input.dispatchEvent(new Event('change'));
+ await aTimeout(20);
+ expect(input.value).to.equal('ABC');
+ expect(code.innerText).to.equal('xABC');
+ });
+
+ it('6. initial slice value from input, button ignored till change on click.', async () =>
+ {
+ const el = await renderStory(InitByInputValueByButton);
+ const input = $('input' , el )
+ , code = $('code' , el );
+
+ expect(input.value).to.equal('anonymous');
+ expect(code.innerText).to.equal('anonymous');
+
+ $('button',el).click()
+ await aTimeout(20);
+ expect(input.value).to.equal('anonymous');
+ expect(code.innerText).to.equal('broccoli');
+ });
+
+ it('7. initial slice value from SLICE element, button ignored till change on click.', async () =>
+ {
+ const el = await renderStory(InitBySliceElement);
+ const btn = $('button' , el )
+ , code = $('code' , el );
+
+ expect(code.innerText).to.equal('0');
+
+ btn.click()
+ await aTimeout(20);
+ expect(code.innerText).to.equal('1');
+ btn.dispatchEvent(new Event('tap'));
+ await aTimeout(20);
+ expect(code.innerText).to.equal('2');
+ btn.click()
+ await aTimeout(20);
+ expect(code.innerText).to.equal('3');
+ });
+
+ it('8. multiple slices by SLICE element, button ignored till change on click.', async () =>
+ {
+ const el = await renderStory(InitByFewSliceElement);
+ const btn = $('button' , el )
+ , code = $('code' , el )
+ , focus = $('var' , el );
+
+ expect(code.innerText).to.equal('0');
+ expect(focus.innerText).to.equal('0');
+
+ btn.dispatchEvent(new Event('focus'));
+ await aTimeout(20);
+ expect(code.innerText).to.equal('0');
+ expect(focus.innerText).to.equal('1');
+
+ btn.click()
+ await aTimeout(20);
+ expect(code.innerText).to.equal('1');
+ expect(focus.innerText).to.equal('1');
+
+ btn.dispatchEvent(new Event('tap'));
+ await aTimeout(20);
+ expect(code.innerText).to.equal('2');
+ expect(focus.innerText).to.equal('1');
+
+ code.click()
+ btn.dispatchEvent(new Event('blur'));
+ await aTimeout(20);
+ expect(code.innerText).to.equal('2');
+ expect(focus.innerText).to.equal('0');
+ });
+
+ it('9. slice in attribute', async () =>
+ {
+ const el = await renderStory(SlicePointsAttribute);
+ const d1 = $('#e1' , el )
+ , d2 = $('#e2' , el )
+ , i1 = $('#e1 input' , el )
+ , i2 = $('#e2 input' , el )
+ , c1 = $('#e1 code' , el )
+ , c2 = $('#e2 code' , el );
+
+ expect(c1.innerText).to.equal(':)');
+ expect(c2.innerText).to.equal('');
+ expect(i1.value).to.equal(':)');
+ expect(i2.value).to.equal('😃');
+ expect(d1.getAttribute('emotion')).to.equal(':)');
+ expect(d2.getAttribute('emotion')).to.equal('😃');
+
+ i1.value="AB";
+ i1.dispatchEvent(new Event('change'));
+ await aTimeout(20);
+ expect(c1.innerText).to.equal('AB');
+ expect(c2.innerText).to.equal('');
+ expect(i1.value).to.equal('AB');
+ expect(d1.getAttribute('emotion')).to.equal('AB');
+ expect(d2.getAttribute('emotion')).to.equal('😃');
+ });
+});
diff --git a/test/dom-merge.test.js b/test/dom-merge.test.js
index dd28ec6..ab1c328 100644
--- a/test/dom-merge.test.js
+++ b/test/dom-merge.test.js
@@ -35,11 +35,11 @@ describe('dom-merge', () => {
expect( txt('b') ).to.equal(TXT.length+'' );
});
- it('LocalStorageLoad slot change on blur', async () => {
+ it('input value, type and observe char count update on keyup', async () => {
const {el, $,$$,txt} = await renderStory(InputOnChange);
await aTimeout(10); // wait for update from slice
expect( txt('b[data-dce-id]') ) // from instance instead of in-template
- .to.equal(`Type time update`.length+'' );
+ .to.equal('0' );
const ta = $('input[data-dce-id]');
ta.value = TXT;
ta.dispatchEvent( new Event('keyup') );
diff --git a/test/http-request.test.js b/test/http-request.test.js
index 74dfd11..a7b061d 100644
--- a/test/http-request.test.js
+++ b/test/http-request.test.js
@@ -27,14 +27,12 @@ describe('http-request', () =>
await aTimeout(50);
expect(el.querySelectorAll('[data-testid="request-section"]').length).to.equal(1);
const attrs = [...el.querySelectorAll('[data-testid="section-attribute"]')]
- expect(attrs.length).to.equal(4);
+ expect(attrs.length).to.equal(3);
expect(attrs[0].innerText).to.equal('@url');
expect(attrs[0].nextElementSibling.nextElementSibling.innerText).to.equal('https://pokeapi.co/api/v2/noreturn');
- expect(attrs[1].innerText).to.equal('@slice');
- expect(attrs[1].nextElementSibling.nextElementSibling.innerText).to.equal('request_slice');
- expect(attrs[2].innerText).to.equal('@type');
- expect(attrs[2].nextElementSibling.nextElementSibling.innerText).to.equal('text');
- expect(attrs[3].innerText).to.equal('@data-dce-id');
+ expect(attrs[1].innerText).to.equal('@type');
+ expect(attrs[1].nextElementSibling.nextElementSibling.innerText).to.equal('text');
+ expect(attrs[2].innerText).to.equal('@data-dce-id');
expect(el.innerText).to.include('\nrequest');
expect(el.innerText).not.to.include('\nresponse');
});
@@ -55,7 +53,6 @@ describe('http-request', () =>
const nextText = ()=> (attr = attr.nextElementSibling).innerText;
expect( nextText() ).to.equal('@url = https://pokeapi.co/api/v2/reflect');
- expect( nextText() ).to.equal('@slice = request_slice');
expect( nextText() ).to.equal('@type = text');
expect( nextText() ).to.equal('@mode = cors');
expect( nextText() ).to.include('@data-dce-id');