1 | const R = require('ramda')
|
2 | const fs = require('fs')
|
3 | const path = require('path')
|
4 | const debug = require('debug')('spdt')
|
5 | const { getConfig, STORYBOOK_PORT, SPDT_DIR } = require('./config')
|
6 |
|
7 | const ROOT_SELECTOR = '[id=root]'
|
8 | const saveFile = (fileName, content) => {
|
9 | fs.writeFile(fileName, content, (err) => {
|
10 | if (err) {
|
11 | return debug(`error: ${err}`)
|
12 | } else {
|
13 | return debug(`File was saved: ${fileName}`)
|
14 | }
|
15 | })
|
16 | }
|
17 |
|
18 | const generateImports = () => `const TIMEOUT = 5000
|
19 | `
|
20 |
|
21 | const generateDescribe = (componentName, fixtureName, itTests) => `
|
22 | describe(
|
23 | '${componentName} - fixture ${fixtureName}',
|
24 | () => {
|
25 | let page
|
26 | let iFrame
|
27 | beforeAll(async () => {
|
28 | // eslint-disable-next-line no-underscore-dangle
|
29 | page = await global.__BROWSER__.newPage()
|
30 | // prettier-ignore
|
31 | await page.goto('http://localhost:${STORYBOOK_PORT}/?selectedKind=${componentName}&selectedStory=${fixtureName}')
|
32 | const frames = await page.frames()
|
33 | iFrame = frames.find((f) => f.name() === 'storybook-preview-iframe')
|
34 | }, TIMEOUT)
|
35 |
|
36 | afterAll(async () => {
|
37 | await page.close()
|
38 | })
|
39 | ${itTests}
|
40 | },
|
41 | TIMEOUT,
|
42 | )
|
43 | `
|
44 |
|
45 | const testCheckSelectorObject = (strOrObj) => {
|
46 | let selector
|
47 | let length
|
48 | if (typeof strOrObj === 'string') {
|
49 | selector = strOrObj
|
50 | length = 1
|
51 | } else if (typeof strOrObj === 'object' && !Array.isArray(strOrObj)) {
|
52 | ;({ length, selector } = strOrObj)
|
53 | }
|
54 |
|
55 | if (!selector || !length) {
|
56 | return null
|
57 | }
|
58 | return `
|
59 | it('checkSelector: should find component matching selector [${selector}] ${length} time(s)', async () => {
|
60 | const components = await iFrame.$$('${ROOT_SELECTOR} ${selector}')
|
61 | const expected = ${length}
|
62 | expect(components).toHaveLength(expected)
|
63 | })`
|
64 | }
|
65 |
|
66 | const testCheckSelector = (fixture) => {
|
67 | const { checkSelector } = R.propOr({}, 'spdt', fixture)
|
68 | if (!checkSelector) {
|
69 | return null
|
70 | }
|
71 | if (typeof checkSelector === 'string') {
|
72 | return testCheckSelectorObject(checkSelector)
|
73 | }
|
74 | if (Array.isArray(checkSelector)) {
|
75 | return checkSelector.map(testCheckSelectorObject)
|
76 | } else if (typeof checkSelector === 'object') {
|
77 | return testCheckSelectorObject(checkSelector)
|
78 | }
|
79 | return null
|
80 | }
|
81 |
|
82 | const testCheckSvg = (fixture) => {
|
83 | const { checkSvg } = R.propOr({}, 'spdt', fixture)
|
84 | if (checkSvg !== true) {
|
85 | return null
|
86 | }
|
87 | return `
|
88 | it('checkSvg: should load component as <svg>', async () => {
|
89 | const component = await iFrame.$('svg')
|
90 | expect(component._remoteObject.description).toMatch('svg') // eslint-disable-line no-underscore-dangle
|
91 | })`
|
92 | }
|
93 |
|
94 | const testCheckAxes = (fixture) => {
|
95 | const { checkAxes } = R.propOr({}, 'spdt', fixture)
|
96 | if (!Number.isInteger(checkAxes)) {
|
97 | return null
|
98 | }
|
99 | return `
|
100 | it('checkAxes: should have ${checkAxes} axes', async () => {
|
101 | const axes = await iFrame.$$('${ROOT_SELECTOR} g.axis')
|
102 | const expected = ${checkAxes}
|
103 | expect(axes).toHaveLength(expected)
|
104 | })`
|
105 | }
|
106 |
|
107 | const testCheckBars = (fixture) => {
|
108 | const { checkBars } = R.propOr({}, 'spdt', fixture)
|
109 | if (checkBars !== true) {
|
110 | return null
|
111 | }
|
112 | const checkBarsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
|
113 | return `
|
114 | it('checkBars: should have ${checkBarsValue} bars according to fixture data', async () => {
|
115 | const bars = await iFrame.$$('${ROOT_SELECTOR} rect.bar')
|
116 | const expected = ${checkBarsValue} // fixture.props.data.length
|
117 | expect(bars).toHaveLength(expected)
|
118 | })`
|
119 | }
|
120 |
|
121 | const testCheckArcs = (fixture) => {
|
122 | const { checkArcs } = R.propOr({}, 'spdt', fixture)
|
123 | if (checkArcs !== true) {
|
124 | return null
|
125 | }
|
126 | const checkArcsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
|
127 | return `
|
128 | it('checkArcs: should have ${checkArcsValue} arcs according to fixture data', async () => {
|
129 | const arcs = await iFrame.$$('${ROOT_SELECTOR} path.arc')
|
130 | const expected = ${checkArcsValue} // fixture.props.data.length
|
131 | expect(arcs).toHaveLength(expected)
|
132 | })`
|
133 | }
|
134 |
|
135 | const config = getConfig()
|
136 | let customMapper
|
137 | try {
|
138 | const mapperPath = path.resolve(config.projectRoot, SPDT_DIR, 'test-declarations.js')
|
139 |
|
140 | customMapper = require(mapperPath)
|
141 | } catch (error) {
|
142 | debug(`Caught exception: ${error.message}`)
|
143 | throw error
|
144 | }
|
145 |
|
146 |
|
147 | const testMapper = {
|
148 | checkSelector: testCheckSelector,
|
149 | checkSvg: testCheckSvg,
|
150 | checkAxes: testCheckAxes,
|
151 | checkBars: testCheckBars,
|
152 | checkArcs: testCheckArcs,
|
153 | ...customMapper,
|
154 | }
|
155 |
|
156 | const generateItTests = (fixture) => {
|
157 | const result = []
|
158 | const spdtKeys = Object.keys(R.propOr({}, 'spdt', fixture))
|
159 | spdtKeys.forEach((checkItem) => testMapper[checkItem] && result.push(testMapper[checkItem](fixture)))
|
160 | return result.join('')
|
161 | }
|
162 |
|
163 | function getFileName({ file, pathToTestIndex }) {
|
164 | const pathTo = pathToTestIndex || getConfig().pathToTestIndex
|
165 | return `${pathTo}/${file.replace('.story.', '.generated.spdt.')}`
|
166 | }
|
167 |
|
168 | function generateContent({ fixtures, componentName }) {
|
169 | const content = []
|
170 | content.push(generateImports(componentName))
|
171 |
|
172 | Object.keys(fixtures).forEach((fixtureName) => {
|
173 | const itTests = generateItTests(fixtures[fixtureName])
|
174 | content.push(generateDescribe(componentName, fixtureName, itTests))
|
175 | })
|
176 | return content
|
177 | }
|
178 |
|
179 | function testGenerator({ fixtures, file, componentName, pathToTestIndex }) {
|
180 | const content = generateContent({ fixtures, componentName })
|
181 | const fileName = getFileName({ file, pathToTestIndex })
|
182 | saveFile(fileName, content.join(''))
|
183 | }
|
184 |
|
185 | module.exports = {
|
186 | testGenerator,
|
187 | getFileName,
|
188 | generateContent,
|
189 | testCheckArcs,
|
190 | testCheckBars,
|
191 | testCheckSvg,
|
192 | testCheckSelector,
|
193 | testCheckAxes,
|
194 | saveFile,
|
195 | ROOT_SELECTOR,
|
196 | }
|