Skip to content

Commit 06c7e8c

Browse files
krakenfusspago
authored andcommitted
Melody streams (trivago#102)
* Add new package melody-streams * Add tests for attachEvent, withElement, createState * Operator tests done * First component api tests * Refactoring after comments. Adding more tests on component spec. * Add combineRefs * Add melody-streams to changelog * More detailed tests on combineRefs * Fix combineRefs spec description * Refactoring after code review. Adding render from melody-component to this package.
1 parent 36d7768 commit 06c7e8c

29 files changed

+1796
-1
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ node_modules
33
.DS_STORE
44
.idea
55
.vscode
6+
.history
67
*-debug.log
78
/build
89
/.nyc_output

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Introduce `useAtom` hook [#79](https://github.com/trivago/melody/pulls/79)
1212
- Introduce `useStore` hook and performance marks for hooks [#98](https://github.com/trivago/melody/pulls/98)
1313
- Added async mounting of components [#82](https://github.com/trivago/melody/pull/82)
14+
- Introduce `melody-streams` API [#102](https://github.com/trivago/melody/pull/102)
1415

1516
### Fixes
1617

Diff for: package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@
9393
"*.[jt]s": [
9494
"prettier",
9595
"git add"
96-
]
96+
],
97+
"ignore": ["./.history"]
9798
},
9899
"bundlesize": [
99100
{

Diff for: packages/melody-streams/.npmignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**/__tests__/**
2+
yarn.lock
+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { shallowEqual } from '../src/util/shallowEqual';
2+
3+
describe('util', () => {
4+
describe('shallowEqual', () => {
5+
describe('objects', () => {
6+
it('should compare objects', () => {
7+
expect(
8+
shallowEqual(
9+
{ a: 1, b: 2, c: undefined },
10+
{ a: 1, b: 2, c: undefined }
11+
)
12+
).toBe(true);
13+
14+
expect(
15+
shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })
16+
).toBe(true);
17+
18+
const o = {};
19+
expect(
20+
shallowEqual({ a: 1, b: 2, c: o }, { a: 1, b: 2, c: o })
21+
).toBe(true);
22+
23+
const d = function() {
24+
return 1;
25+
};
26+
expect(
27+
shallowEqual(
28+
{ a: 1, b: 2, c: o, d },
29+
{ a: 1, b: 2, c: o, d }
30+
)
31+
).toBe(true);
32+
33+
expect(
34+
shallowEqual(
35+
{
36+
a: 1,
37+
b: 2,
38+
d: function() {
39+
return 1;
40+
},
41+
},
42+
{
43+
a: 1,
44+
b: 2,
45+
d: function() {
46+
return 1;
47+
},
48+
}
49+
)
50+
).toBe(false);
51+
expect(shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })).toBe(
52+
false
53+
);
54+
expect(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(
55+
false
56+
);
57+
expect(
58+
shallowEqual(
59+
{ a: 1, b: 2, c: undefined },
60+
{ a: 1, bb: 2, c: undefined }
61+
)
62+
).toBe(false);
63+
});
64+
65+
it('should compare empty objects, with false', () => {
66+
expect(shallowEqual({}, false)).toBe(false);
67+
expect(shallowEqual(false, {})).toBe(false);
68+
expect(shallowEqual([], false)).toBe(false);
69+
expect(shallowEqual(false, [])).toBe(false);
70+
});
71+
});
72+
73+
describe('arrays', () => {
74+
it('should compare arrays', () => {
75+
expect(shallowEqual([], [])).toBe(true);
76+
expect(shallowEqual([1], [1])).toBe(true);
77+
expect(shallowEqual([1, 2], [1, 2])).toBe(true);
78+
expect(shallowEqual([1, 2], [1, 2, 3])).toBe(false);
79+
expect(shallowEqual([1, 2, 3], [1, 2])).toBe(false);
80+
const o = {};
81+
expect(shallowEqual([1, o], [1, o])).toBe(true);
82+
const fn = () => {};
83+
expect(shallowEqual([1, fn], [1, fn])).toBe(true);
84+
expect(shallowEqual([1, 2], [2, 1])).toBe(false);
85+
expect(shallowEqual([1, {}], [1, {}])).toBe(false);
86+
expect(shallowEqual([1, () => {}], [1, () => {}])).toBe(false);
87+
expect(shallowEqual([], '')).toBe(false);
88+
expect(shallowEqual([], 0)).toBe(false);
89+
expect(shallowEqual([], {})).toBe(false);
90+
expect(shallowEqual([], false)).toBe(false);
91+
expect(shallowEqual([], NaN)).toBe(false);
92+
});
93+
});
94+
95+
describe('numbers', () => {
96+
it('should compare numbers', () => {
97+
expect(shallowEqual(1337, 1337)).toBe(true);
98+
expect(shallowEqual(1337, -1337)).toBe(false);
99+
expect(shallowEqual(1337, 2)).toBe(false);
100+
expect(shallowEqual(0, 0)).toBe(true);
101+
expect(shallowEqual(-0, 0)).toBe(false);
102+
expect(shallowEqual(0, {})).toBe(false);
103+
expect(shallowEqual(0, [])).toBe(false);
104+
expect(shallowEqual(0, '')).toBe(false);
105+
expect(shallowEqual(NaN, NaN)).toBe(true);
106+
expect(shallowEqual(0, NaN)).toBe(false);
107+
});
108+
});
109+
110+
describe('strings', () => {
111+
it('should compare numbers', () => {
112+
expect(shallowEqual('', '')).toBe(true);
113+
expect(shallowEqual('foo', 'foo')).toBe(true);
114+
expect(shallowEqual('foo', 'bar')).toBe(false);
115+
expect(shallowEqual('', 'bar')).toBe(false);
116+
expect(shallowEqual('bar', '')).toBe(false);
117+
expect(shallowEqual('', false)).toBe(false);
118+
expect(shallowEqual('', 0)).toBe(false);
119+
expect(shallowEqual('', [])).toBe(false);
120+
expect(shallowEqual('', {})).toBe(false);
121+
expect(shallowEqual('', NaN)).toBe(false);
122+
});
123+
});
124+
125+
describe('functions', () => {
126+
it('should compare functions', () => {
127+
const fn = () => {};
128+
expect(shallowEqual(fn, fn)).toBe(true);
129+
expect(shallowEqual(fn, () => {})).toBe(false);
130+
});
131+
});
132+
133+
describe('booleans', () => {
134+
it('should compare booleans', () => {
135+
expect(shallowEqual(false, false)).toBe(true);
136+
expect(shallowEqual(true, true)).toBe(true);
137+
expect(shallowEqual(false, true)).toBe(false);
138+
expect(shallowEqual(true, false)).toBe(false);
139+
});
140+
});
141+
142+
describe('falsy values', () => {
143+
it('should compare falsy values', () => {
144+
expect(shallowEqual(false, false)).toBe(true);
145+
expect(shallowEqual(false, undefined)).toBe(false);
146+
expect(shallowEqual(false, null)).toBe(false);
147+
expect(shallowEqual(false, 0)).toBe(false);
148+
expect(shallowEqual(false, '')).toBe(false);
149+
expect(shallowEqual(false, NaN)).toBe(false);
150+
151+
expect(shallowEqual(null, null)).toBe(true);
152+
expect(shallowEqual(null, undefined)).toBe(false);
153+
expect(shallowEqual(null, false)).toBe(false);
154+
expect(shallowEqual(null, 0)).toBe(false);
155+
expect(shallowEqual(null, '')).toBe(false);
156+
expect(shallowEqual(null, NaN)).toBe(false);
157+
158+
expect(shallowEqual(undefined, undefined)).toBe(true);
159+
expect(shallowEqual(undefined, null)).toBe(false);
160+
expect(shallowEqual(undefined, false)).toBe(false);
161+
expect(shallowEqual(undefined, 0)).toBe(false);
162+
expect(shallowEqual(undefined, '')).toBe(false);
163+
expect(shallowEqual(undefined, NaN)).toBe(false);
164+
});
165+
});
166+
});
167+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`attachEvent should attach a click handler 1`] = `"[{\\"type\\":\\"click\\"},{\\"type\\":\\"click\\"},{\\"type\\":\\"click\\"}]"`;
4+
5+
exports[`attachEvent should attach multiple handlers 1`] = `"[{\\"type\\":\\"click\\"},{\\"type\\":\\"click\\"},{\\"type\\":\\"click\\"},{\\"type\\":\\"mouseenter\\"},{\\"type\\":\\"mouseenter\\"},{\\"type\\":\\"mouseenter\\"}]"`;

Diff for: packages/melody-streams/__tests__/attachEventSpec.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copyright 2019 trivago N.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS-IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { attachEvent } from '../src';
18+
import { applyGradualyAndComplete } from './util/testHelpers';
19+
import { createMouseEvent } from './util/mouseEvent';
20+
21+
const dispatchClick = createMouseEvent('click');
22+
const dispatchMouseEnter = createMouseEvent('mouseenter');
23+
24+
describe('attachEvent', () => {
25+
it('should attach a click handler', async () => {
26+
const el = document.createElement('div');
27+
const [refHandler, subj] = attachEvent('click');
28+
refHandler(el);
29+
applyGradualyAndComplete(subj, dispatchClick(el), [
30+
undefined,
31+
undefined,
32+
undefined,
33+
]).then(handlers => expect(JSON.stringify(handlers)).toMatchSnapshot());
34+
});
35+
36+
it('should attach multiple handlers', async () => {
37+
const el = document.createElement('div');
38+
const [refHandler, subj] = attachEvent('click', 'mouseenter');
39+
refHandler(el);
40+
applyGradualyAndComplete(
41+
subj,
42+
[dispatchClick(el), dispatchMouseEnter(el)],
43+
[undefined, undefined, undefined]
44+
).then(handlers => expect(JSON.stringify(handlers)).toMatchSnapshot());
45+
});
46+
});

Diff for: packages/melody-streams/__tests__/combineRefsSpec.js

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Copyright 2019 trivago N.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS-IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { combineRefs, withElement } from '../src';
18+
import { applyGradualyAndComplete, next } from './util/testHelpers';
19+
import { Subject } from 'rxjs';
20+
21+
const wrappedWithElement = obs => withElement(el => obs);
22+
23+
describe('combineRefs', () => {
24+
it('should not update subjects when combinedRefs was not executed after being attached to handlers', async () => {
25+
const sink = new Subject();
26+
const [refHandler1, subj1] = wrappedWithElement(sink);
27+
const [refHandler2, subj2] = wrappedWithElement(sink);
28+
const combinedRefs = combineRefs(refHandler1, refHandler2);
29+
expect(typeof combinedRefs).toBe('function');
30+
applyGradualyAndComplete([subj1, subj2], next(sink), [
31+
'foo',
32+
'bar',
33+
]).then(([stream1, stream2]) => {
34+
expect(stream1).toEqual([]);
35+
expect(stream2).toEqual([]);
36+
});
37+
});
38+
it('should combine refs and attach them to el bound to same stream while subjects should have same values', async () => {
39+
const sink = new Subject();
40+
const el = document.createElement('div');
41+
const [refHandler1, subj1] = wrappedWithElement(sink);
42+
const [refHandler2, subj2] = wrappedWithElement(sink);
43+
const combinedRefs = combineRefs(refHandler1, refHandler2);
44+
expect(typeof combinedRefs).toBe('function');
45+
const combinedRefHandlers = combinedRefs(el);
46+
expect(typeof combinedRefHandlers).toBe('object');
47+
expect(combinedRefHandlers).toHaveProperty('unsubscribe');
48+
49+
applyGradualyAndComplete([subj1, subj2], next(sink), [
50+
'foo',
51+
'bar',
52+
]).then(([stream1, stream2]) => {
53+
expect(stream1).toEqual(['foo', 'bar']);
54+
expect(stream2).toEqual(['foo', 'bar']);
55+
});
56+
});
57+
it('should combine refs and attach them to el bound to different strem while subjects should have different values', async () => {
58+
const sink1 = new Subject();
59+
const sink2 = new Subject();
60+
const el = document.createElement('div');
61+
const [refHandler1, subj1] = wrappedWithElement(sink1);
62+
const [refHandler2, subj2] = wrappedWithElement(sink2);
63+
const combinedRefs = combineRefs(refHandler1, refHandler2);
64+
expect(typeof combinedRefs).toBe('function');
65+
const combinedRefHandlers = combinedRefs(el);
66+
expect(typeof combinedRefHandlers).toBe('object');
67+
expect(combinedRefHandlers).toHaveProperty('unsubscribe');
68+
69+
applyGradualyAndComplete(
70+
[subj1, subj2],
71+
[...next(sink1), ...next(sink2)],
72+
[['foo', 'bar'], ['biz', 'baz']]
73+
).then(([stream1, stream2]) => {
74+
expect(stream1).toEqual(['foo', 'bar']);
75+
expect(stream2).toEqual(['biz', 'baz']);
76+
});
77+
});
78+
});

0 commit comments

Comments
 (0)