UNPKG

4.25 kBJavaScriptView Raw
1/* @flow */
2
3import _ from 'lodash'
4import fs from 'fs-extra'
5import path from 'path'
6import temp from 'temp'
7
8import { DiCy, File, Rule } from '../src/main'
9import type { Command, Event, Phase } from '../src/types'
10
11export async function cloneFixtures () {
12 const tempPath = fs.realpathSync(temp.mkdirSync('dicy'))
13 let fixturesPath = path.resolve(__dirname, 'fixtures')
14 await File.copy(fixturesPath, tempPath)
15 return tempPath
16}
17
18function formatMessage (event: Event) {
19 switch (event.type) {
20 case 'command':
21 return ` [${event.rule}] Executing command \`${event.command}\``
22 case 'action':
23 const triggerText = event.triggers.length === 0 ? '' : ` triggered by ${event.triggers.join(', ')}`
24 return ` [${event.rule}] Evaluating ${event.action}${triggerText}`
25 case 'log':
26 const parts = []
27
28 if (event.name) parts.push(`[${event.name}]`)
29 if (event.category) parts.push(`${event.category}:`)
30 parts.push(event.text)
31
32 return ` ${parts.join(' ')}`
33 }
34}
35
36function constructMessage (found: Array<Event>, missing: Array<Event>) {
37 const lines = []
38 if (found.length !== 0) {
39 lines.push('Did not expect the following events:', ...found.map(formatMessage))
40 }
41 if (missing.length !== 0) {
42 lines.push('Expected the following events:', ...missing.map(formatMessage))
43 }
44 return lines.join('\n')
45}
46
47function compareFilePaths (x: string, y: string): boolean {
48 return path.normalize(x) === path.normalize(y) || ((path.isAbsolute(x) || path.isAbsolute(y)) && path.basename(x) === path.basename(y))
49}
50
51function stringCompare (x: string, y: string): boolean {
52 return x.replace(/[/\\'"^]/g, '') === y.replace(/[/\\'"^]/g, '')
53}
54
55export function partitionMessages (received: Array<Event>, expected: Array<Event>) {
56 let proper = []
57 let improper = []
58 let missing = _.uniqWith(expected, _.isEqual)
59
60 for (const event of _.uniqWith(received, _.isEqual)) {
61 let index = missing.findIndex(candidate =>
62 _.isMatchWith(event, candidate, (x, y, key) => key === 'file'
63 ? compareFilePaths(x, y)
64 : ((typeof x === 'string' && typeof y === 'string')
65 ? stringCompare(x, y)
66 : undefined)))
67 if (index === -1) {
68 improper.push(event)
69 } else {
70 proper.push(event)
71 missing.splice(index, 1)
72 }
73 }
74
75 return { proper, improper, missing }
76}
77
78export const customMatchers = {
79 toReceiveEvents (util: Object, customEqualityTesters: Object) {
80 return {
81 compare: function (received: Array<Event>, expected: Array<Event>) {
82 const { proper, improper, missing } = partitionMessages(received, expected)
83
84 const pass = improper.length === 0 && missing.length === 0
85 const message = pass
86 ? constructMessage(proper, [])
87 : constructMessage(improper, missing)
88
89 return { pass, message }
90 }
91 }
92 }
93}
94
95export type ParameterDefinition = {
96 filePath: string,
97 value?: any
98}
99
100export type RuleDefinition = {
101 RuleClass?: Class<Rule>,
102 command?: Command,
103 phase?: Phase,
104 jobName?: string,
105 filePath?: string,
106 parameters?: Array<ParameterDefinition>,
107 options?: Object,
108 targets?: Array<string>
109}
110
111export async function initializeRule ({ RuleClass, command, phase, jobName, filePath = 'file-types/LaTeX_article.tex', parameters = [], options = {}, targets = [] }: RuleDefinition) {
112 if (!RuleClass) throw new Error('Missing rule class in initializeRule.')
113
114 options.ignoreUserOptions = true
115 const realFilePath = path.resolve(__dirname, 'fixtures', filePath)
116 const dicy = await DiCy.create(realFilePath, options)
117 const files = []
118
119 for (const target of targets) {
120 dicy.addTarget(target)
121 }
122
123 for (const { filePath, value } of parameters) {
124 const file = await dicy.getFile(filePath)
125 if (file && value) file.value = value
126 files.push(file)
127 }
128
129 if (!command) {
130 command = RuleClass.commands.values().next().value || 'build'
131 }
132
133 if (!phase) {
134 phase = RuleClass.phases.values().next().value || 'execute'
135 }
136 const jobOptions = dicy.state.getJobOptions(jobName)
137 const rule = new RuleClass(dicy.state, command, phase, jobOptions, ...files)
138
139 spyOn(rule, 'log')
140 await rule.initialize()
141
142 return { dicy, rule, options: jobOptions }
143}