Skip to content

Commit e978c7f

Browse files
feat: add testing & bench, fix issue #2, refactor
BREAKING CHANGE: default export has been removed in favor of named export BREAKING CHANGE: right-most arguments were not originally given precedence in all cases. This has been fixed.
1 parent d5c6feb commit e978c7f

22 files changed

+1805
-142
lines changed

.editorconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Unix-style newlines with a newline ending every file
7+
[*]
8+
charset = utf-8
9+
end_of_line = lf
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
indent_style = tab
13+
indent_size = 2
14+
max_line_length = 180

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
node_modules/
22
.*
33
!.gitignore
4+
!.editorconfig
5+
coverage/

src/index.test.ts renamed to __test__/array-merge.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { VNodeData, VNodeDirective } from "vue"
2-
import { mergeData } from "./index"
2+
import { mergeData } from "../src/index"
33

44
it("should execute array merge on class, style, directive properties", () => {
55
let vd1: VNodeData = {
@@ -13,8 +13,8 @@ it("should execute array merge on class, style, directive properties", () => {
1313

1414
let actual = mergeData(vd1, vd2)
1515
let expected = {
16-
class: ["a", { b: true, c: false }, "d", { e: true, f: false }],
17-
style: ["display:block;", { color: "red", fontSize: "16px" }, "position:absolute;"],
16+
class: ["d", { e: true, f: false }, "a", { b: true, c: false }],
17+
style: ["position:absolute;", "display:block;", { color: "red", fontSize: "16px" }],
1818
}
1919

2020
// Check values recursively
@@ -27,3 +27,14 @@ it("should execute array merge on class, style, directive properties", () => {
2727
expect(actual.style).not.toBe(vd1.style)
2828
expect(actual.style).not.toBe(vd2.style)
2929
})
30+
31+
it("should init types to array", () => {
32+
let test = mergeData({ class: "string" })
33+
expect(Array.isArray(test.class)).toBe(true)
34+
35+
test = mergeData({ class: { string: true } })
36+
expect(Array.isArray(test.class)).toBe(true)
37+
38+
test = mergeData({ class: ["string"] })
39+
expect(Array.isArray(test.class)).toBe(true)
40+
})

__test__/handler-merge.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { VNodeData, VNodeDirective } from "vue"
2+
import { mergeData } from "../src/index"
3+
4+
it("should not mutate original object (issue #2)", () => {
5+
let def1 = { on: { click() {} } }
6+
let def2 = { on: { click() {} } }
7+
8+
let onclick1 = def1.on.click
9+
let onclick2 = def2.on.click
10+
11+
let data = mergeData({}, def1, def2)
12+
13+
expect(def1.on).not.toBe(data.on)
14+
expect(def2.on).not.toBe(data.on)
15+
16+
expect(def1.on.click).toBe(onclick1)
17+
expect(def2.on.click).toBe(onclick2)
18+
19+
expect(data.on.click).toContain(onclick1)
20+
expect(data.on.click).toContain(onclick2)
21+
})
22+
23+
it("should set single handlers and concat multi", () => {
24+
let h1 = console.log
25+
let h2 = console.info
26+
let h3 = console.error
27+
let actual: VNodeData
28+
29+
actual = mergeData({ class: ["btn", "text-center"] }, { on: { mouseup: h1 } })
30+
expect(actual).toMatchObject({ on: { mouseup: h1 } })
31+
expect(Array.isArray(actual.on.mouseup)).toBe(false)
32+
33+
actual = mergeData({ nativeOn: { mouseup: h1 } }, { class: ["btn", "text-center"] })
34+
expect(actual).toMatchObject({ nativeOn: { mouseup: h1 } })
35+
expect(Array.isArray(actual.nativeOn.mouseup)).toBe(false)
36+
37+
actual = mergeData({ on: { mouseup: h1 } }, { on: { mouseup: h2 } }, { on: { mouseup: h3 } })
38+
expect(Array.isArray(actual.on.mouseup)).toBe(true)
39+
expect(actual.on.mouseup).toContain(h1)
40+
expect(actual.on.mouseup).toContain(h2)
41+
expect(actual.on.mouseup).toContain(h3)
42+
})
43+
44+
it("should call the right-most argument first", () => {
45+
let first = 0
46+
let factory = n => () => {
47+
if (!first) {
48+
first = n
49+
}
50+
}
51+
52+
let actual = mergeData(
53+
{ on: { click: factory(1) } },
54+
{ on: { click: factory(2) } },
55+
{ on: { click: factory(3) } },
56+
{ on: { click: factory(4) } }
57+
)
58+
59+
expect(Array.isArray(actual.on.click)).toBe(true)
60+
for (const fn of actual.on.click) {
61+
fn()
62+
}
63+
expect(first).toBe(4)
64+
})

__test__/kitchen-sink.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { VNodeData, VNodeDirective } from "vue"
2+
import { mergeData } from "../src/index"
3+
4+
it("should handle multiple arguments", () => {
5+
// Pre-define functions so they compare equal
6+
function click() {}
7+
function mouseup() {}
8+
9+
let expected: VNodeData = {
10+
staticClass: "btn ml-auto",
11+
class: [{ "text-center": true }, "btn-block", { "btn-primary": true }],
12+
on: {
13+
click: [click, click],
14+
mouseup: [mouseup, mouseup],
15+
},
16+
}
17+
18+
let actual = mergeData(
19+
{ staticClass: "ml-auto" },
20+
{ staticClass: "btn", class: { "btn-primary": true } },
21+
{ class: ["btn-block"] },
22+
{ on: { click, mouseup } },
23+
{ on: { click, mouseup } },
24+
{ class: { "text-center": true } }
25+
)
26+
27+
expect(actual).toEqual(expected)
28+
})
29+
30+
it("should work like in the example", () => {
31+
let onClick1 = e => alert("💥")
32+
let onClick2 = e => alert("👍")
33+
34+
let componentData: VNodeData = {
35+
staticClass: "fn-component", // concatenates all static classes
36+
class: {
37+
active: true,
38+
"special-class": false,
39+
},
40+
attrs: {
41+
id: "my-functional-component",
42+
},
43+
on: {
44+
click: onClick1,
45+
},
46+
}
47+
// <my-btn variant="primary" type="submit" id="form-submit-btn" @click="onClick">Submit</my-btn>
48+
let templateData: VNodeData = {
49+
attrs: {
50+
id: "form-submit-btn",
51+
type: "submit",
52+
},
53+
on: { click: onClick2 },
54+
}
55+
56+
expect(mergeData(templateData, componentData)).toEqual({
57+
staticClass: "fn-component",
58+
class: expect.arrayContaining([
59+
{
60+
active: true,
61+
"special-class": false,
62+
},
63+
]),
64+
attrs: {
65+
id: "my-functional-component",
66+
type: "submit",
67+
},
68+
on: { click: expect.arrayContaining([onClick1, onClick2]) },
69+
})
70+
})

__test__/reassign.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { VNodeData, VNodeDirective } from "vue"
2+
import { mergeData } from "../src/index"
3+
4+
it("should reassign primitives", () => {
5+
let override: VNodeData = { ref: "winning" }
6+
let vd1: VNodeData = { ref: "ref1" }
7+
let vd2: VNodeData = { ref: "ref2" }
8+
let vd3: VNodeData = { ref: "ref3" }
9+
let vd4: VNodeData = { ref: "ref4" }
10+
11+
let actual = mergeData(vd1, vd2, vd3, vd4, override)
12+
expect(actual.ref).toBe("winning")
13+
})

__test__/string-concat.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { VNodeData, VNodeDirective } from "vue"
2+
import { mergeData } from "../src/index"
3+
4+
it("should concatenate strings", () => {
5+
let test: VNodeData[] = [
6+
{ staticClass: "" },
7+
{ staticClass: "class-1" },
8+
{ staticClass: "class-2" },
9+
{ staticClass: "" },
10+
{ staticClass: "class-3" },
11+
]
12+
let actual = mergeData(...test)
13+
14+
expect(actual.staticClass).toBe("class-3 class-2 class-1")
15+
})

benchmark/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const { mergeData } = require("../dist/lib.common")
2+
const { Suite } = require("benchmark")
3+
4+
let mergeSuite = new Suite("mergeData", {
5+
onCycle(e) {
6+
console.log(e.target.toString())
7+
},
8+
})
9+
10+
let tests = []
11+
12+
tests.push(function basic() {
13+
mergeData(
14+
{
15+
staticClass: "btn",
16+
class: { "btn-primary": true },
17+
},
18+
{ class: ["btn-block"] }
19+
)
20+
})
21+
22+
tests.push(function multi() {
23+
mergeData(
24+
{ staticClass: "btn", class: { "btn-primary": true } },
25+
{ class: ["btn-block"] },
26+
{ on: { click() {}, mouseup() {} } },
27+
{ on: { click() {}, mouseup() {} } },
28+
{ class: { "text-center": true } }
29+
)
30+
})
31+
32+
for (const fn of tests) {
33+
mergeSuite.add(fn.name, fn)
34+
}
35+
36+
mergeSuite.run()

dist/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import { VNodeData } from "vue";
22
declare function mergeData(...vNodeData: VNodeData[]): VNodeData;
3-
export default mergeData;
3+
export { mergeData };

dist/lib.common.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)