UNPKG

5.78 kBJavaScriptView Raw
1const R = require('ramda')
2const fs = require('fs')
3const path = require('path')
4const debug = require('debug')('spdt')
5const { getConfig, STORYBOOK_PORT, SPDT_DIR } = require('./config')
6
7const ROOT_SELECTOR = '[id=root]'
8const 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
18const generateImports = () => `const TIMEOUT = 5000
19`
20
21const generateDescribe = (componentName, fixtureName, itTests) => `
22describe(
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
45const 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
66const 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
82const 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
94const 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
107const 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
121const 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
135const config = getConfig()
136let customMapper
137try {
138 const mapperPath = path.resolve(config.projectRoot, SPDT_DIR, 'test-declarations.js')
139 // eslint-disable-next-line import/no-dynamic-require
140 customMapper = require(mapperPath)
141} catch (error) {
142 debug(`Caught exception: ${error.message}`)
143 throw error
144}
145
146// map a key in fixtures.[fixture name].spdt to respective function which generates an it test
147const testMapper = {
148 checkSelector: testCheckSelector,
149 checkSvg: testCheckSvg,
150 checkAxes: testCheckAxes,
151 checkBars: testCheckBars,
152 checkArcs: testCheckArcs,
153 ...customMapper,
154}
155
156const 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
163function getFileName({ file, pathToTestIndex }) {
164 const pathTo = pathToTestIndex || getConfig().pathToTestIndex
165 return `${pathTo}/${file.replace('.story.', '.generated.spdt.')}`
166}
167
168function 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
179function testGenerator({ fixtures, file, componentName, pathToTestIndex }) {
180 const content = generateContent({ fixtures, componentName })
181 const fileName = getFileName({ file, pathToTestIndex })
182 saveFile(fileName, content.join(''))
183}
184
185module.exports = {
186 testGenerator,
187 getFileName,
188 generateContent,
189 testCheckArcs,
190 testCheckBars,
191 testCheckSvg,
192 testCheckSelector,
193 testCheckAxes,
194 saveFile,
195 ROOT_SELECTOR,
196}