-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
94 lines (85 loc) · 2.82 KB
/
index.js
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
const {
matcherHint,
printReceived,
printExpected,
} = require('jest-matcher-utils');
const parse = require('styled-components/lib/vendor/postcss-safe-parser/parse');
expect.extend({
toHaveStyleRule(component, name, expected) {
const { rules } = component.type();
const props = component.props();
const ast = parse(rules.join());
/**
* Fail by default
*/
let pass = false;
/**
* There can be two cases:
* - rule (dynamic property, value is a function of props)
* - decl (static property, value is a string)
*
* We also take the last matched node because
* developer may override initial assignment
*/
const node = ast.nodes.filter((n) => {
switch (n.type) {
case 'rule':
return n.selector.indexOf(name) === 0;
case 'decl':
return n.prop === name;
default:
return false;
}
}).pop();
let received;
/**
* If node is not found (typo in the rule name /
* rule isn't specified for component), we return
* a special message
*/
if (!node) {
const error = `${name} isn't in the style rules`;
return {
message: () =>
`${matcherHint('.toHaveStyleRule')}'\n\n` +
`Expected ${component.name()} to have a style rule:\n` +
` ${printExpected(`${name}: ${expected}`)}\n` +
'Received:\n' +
` ${printReceived(error)}`,
pass: false,
};
}
/**
* In a case of declaration, it's fairly easy to check if expected === given
*/
if (node.type === 'decl') {
pass = node.value === expected;
received = node.value;
/**
* But in case of rule we have quite some complexity here:
* We can't get a ref to the function using `postcss-safe-parser`, so
* we have to construct it by ourselves. We also don't know how user called `props`
* in his value function, so we parse the entire CSS block to match its params and body
*
* Once params are matched, we construct a new function and
* invoke it with props, taken from the enzyme
*/
} else {
const match = node.source.input.css.match(new RegExp(`${name}:.*,function (.*){(.*)},;`));
const param = match[1].slice(1, -1);
const fn = Function(param, match[2]);
received = fn(props);
pass = received === expected;
}
const diff = '' +
` ${printExpected(`${name}: ${expected}`)}\n` +
'Received:\n' +
` ${printReceived(`${name}: ${received}`)}`;
const message = pass
? () => `${matcherHint('.not.toHaveStyleRule')}'\n\n` +
`Expected ${component.name()} not to contain:\n${diff}`
: () => `${matcherHint('.toHaveStyleRule')}'\n\n` +
`Expected ${component.name()} to have a style rule:\n${diff}`;
return { message, pass };
},
});