-
-
Notifications
You must be signed in to change notification settings - Fork 269
/
has.ts
130 lines (123 loc) · 3.42 KB
/
has.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { Format } from './format.js'
import { Same } from './same.js'
/**
* The same as {@link tcompare!same.Same}, but ignore any fields present in the
* test object and not present in the expect pattern.
*/
export class Has extends Same {
// don't care about object shape, only that it has
// matching fields of the same type.
simpleMatch() {
this.simple = this.test()
if (!this.simple) {
this.unmatch()
}
}
isArray() {
return super.isArray() && new Format(this.expect).isArray()
}
// just return the entries that exist in the expect object
getPojoEntries(obj: any) {
if (obj !== this.object) {
return super.getPojoEntries(obj)
}
const expKeys = this.getPojoKeys(this.expect)
const expSet = new Set(expKeys)
const objKeys = this.getPojoKeys(obj)
const objSet = new Set(objKeys)
for (const k of expKeys) {
if (!objSet.has(k) && this.expect[k] == undefined) {
objKeys.push(k)
}
}
const ent: [PropertyKey, any][] = objKeys
.filter(k => expSet.has(k))
.map(k => [k, obj[k]])
return this.sort ?
ent.sort((a, b) =>
String(a[0]).localeCompare(String(b[0]), 'en'),
)
: ent
}
printMapEntryUnexpected(_key: any, _val: any) {
// nothing to do, this is fine
}
// only test expected array entries within the expect array length
get objectAsArray() {
const arr = super.objectAsArray
if (arr) {
const exp = super.expectAsArray
if (exp && exp.length < arr.length) {
const value = arr.slice(0, exp.length)
Object.defineProperty(this, 'objectAsArray', {
value,
configurable: true,
})
return value
}
}
const value = arr
Object.defineProperty(this, 'objectAsArray', {
value,
configurable: true,
})
return value
}
// always include message/name, so you can do t.has(er, { message }) etc.
printErrorBody() {
super.printErrorBody()
const expKeys = new Set(this.getPojoKeys(this.expect))
if (expKeys.has('name') && this.expect.name) {
this.printPojoEntry('name', this.object.name)
}
if (expKeys.has('message') && this.expect.message) {
this.printPojoEntry('message', this.object.message)
}
}
// this one is a little tricky, because we need to only walk the ones that
// actually exist in the expect set.
printSetBody() {
// if there are MORE items in the expectation, that's always a fail.
if (this.expect.size > this.object.size) {
this.unmatch()
this.memo += this.simplePrint(this.object)
this.memoExpect += this.simplePrint(this.expect)
return
}
const seen = new Set()
// skip all identity matches
for (const val of this.expect) {
if (this.object.has(val)) {
seen.add(val)
continue
}
}
for (const exp of this.expect) {
if (seen.has(exp)) {
continue
}
let sawMatch = false
for (const val of this.object) {
if (seen.has(val)) {
continue
}
const s = this.child(val, {
expect: exp,
provisional: true,
})
s.print()
if (s.match) {
sawMatch = true
seen.add(val)
break
}
}
if (!sawMatch) {
this.unmatch()
this.memo += this.simplePrint(this.object)
this.memoExpect += this.simplePrint(this.expect)
return
}
}
}
}