UNPKG

10.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.module = void 0;
4const tslib_1 = require("tslib");
5const inversify_1 = require("inversify");
6const ymir_1 = require("@fimbul/ymir");
7const base_1 = require("./base");
8const cached_file_system_1 = require("../services/cached-file-system");
9const baseline_1 = require("../baseline");
10const path = require("path");
11const chalk = require("chalk");
12const utils_1 = require("../utils");
13const glob = require("glob");
14const semver_1 = require("semver");
15const runner_1 = require("../runner");
16const ts = require("typescript");
17const diff = require("diff");
18const argparse_1 = require("../argparse");
19const optparse_1 = require("../optparse");
20const TEST_OPTION_SPEC = {
21 ...argparse_1.GLOBAL_OPTIONS_SPEC,
22 fix: optparse_1.OptionParser.Transform.noDefault(argparse_1.GLOBAL_OPTIONS_SPEC.fix),
23 typescriptVersion: optparse_1.OptionParser.Factory.parsePrimitive('string'),
24};
25let FakeDirectoryService = class FakeDirectoryService {
26 constructor(realDirectorySerivce) {
27 this.realDirectorySerivce = realDirectorySerivce;
28 }
29 getCurrentDirectory() {
30 return this.cwd;
31 }
32 getRealCurrentDirectory() {
33 return this.realDirectorySerivce.getCurrentDirectory();
34 }
35};
36FakeDirectoryService = tslib_1.__decorate([
37 inversify_1.injectable(),
38 tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService])
39], FakeDirectoryService);
40let TestCommandRunner = class TestCommandRunner extends base_1.AbstractCommandRunner {
41 constructor(runner, fs, logger, directoryService) {
42 super();
43 this.runner = runner;
44 this.fs = fs;
45 this.logger = logger;
46 this.directoryService = directoryService;
47 }
48 run(options) {
49 const currentTypescriptVersion = getNormalizedTypescriptVersion();
50 const basedir = this.directoryService.getRealCurrentDirectory();
51 let baselineDir;
52 let root;
53 let baselinesSeen;
54 let success = true;
55 const host = {
56 checkResult: (file, kind, summary) => {
57 const relative = path.relative(root, file);
58 if (relative.startsWith('..' + path.sep))
59 throw new ymir_1.ConfigurationError(`Testing file '${file}' outside of '${root}'.`);
60 const actual = kind === "fix" /* Fix */ ? summary.content : baseline_1.createBaseline(summary);
61 const baselineFile = `${path.resolve(baselineDir, relative)}.${kind}`;
62 const end = (pass, text, baselineDiff) => {
63 this.logger.log(` ${chalk.grey.dim(path.relative(basedir, baselineFile))} ${chalk[pass ? 'green' : 'red'](text)}`);
64 if (pass)
65 return true;
66 if (baselineDiff !== undefined)
67 this.logger.log(baselineDiff);
68 success = false;
69 return !options.bail;
70 };
71 if (kind === "fix" /* Fix */ && summary.fixes === 0) {
72 if (!this.fs.isFile(baselineFile))
73 return true;
74 if (options.updateBaselines) {
75 this.fs.remove(baselineFile);
76 return end(true, 'REMOVED');
77 }
78 baselinesSeen.push(utils_1.unixifyPath(baselineFile));
79 return end(false, 'EXISTS');
80 }
81 baselinesSeen.push(utils_1.unixifyPath(baselineFile));
82 let expected;
83 try {
84 expected = this.fs.readFile(baselineFile);
85 }
86 catch {
87 if (!options.updateBaselines)
88 return end(false, 'MISSING');
89 this.fs.createDirectory(path.dirname(baselineFile));
90 this.fs.writeFile(baselineFile, actual);
91 return end(true, 'CREATED');
92 }
93 if (expected === actual)
94 return end(true, 'PASSED');
95 if (options.updateBaselines) {
96 this.fs.writeFile(baselineFile, actual);
97 return end(true, 'UPDATED');
98 }
99 return end(false, 'FAILED', createBaselineDiff(actual, expected));
100 },
101 };
102 const globOptions = {
103 absolute: true,
104 cache: {},
105 nodir: true,
106 realpathCache: {},
107 statCache: {},
108 symlinks: {},
109 cwd: basedir,
110 };
111 for (const pattern of options.files) {
112 for (const testcase of glob.sync(pattern, globOptions)) {
113 const { typescriptVersion, ...testConfig } = optparse_1.OptionParser.parse(require(testcase), TEST_OPTION_SPEC, { validate: true, context: testcase, exhaustive: true });
114 if (typescriptVersion !== undefined && !semver_1.satisfies(currentTypescriptVersion, typescriptVersion)) {
115 this.logger.log(`${path.relative(basedir, testcase)} ${chalk.yellow(`SKIPPED, requires TypeScript ${typescriptVersion}`)}`);
116 continue;
117 }
118 root = path.dirname(testcase);
119 baselineDir = buildBaselineDirectoryName(basedir, 'baselines', testcase);
120 this.logger.log(path.relative(basedir, testcase));
121 this.directoryService.cwd = root;
122 baselinesSeen = [];
123 if (!this.test(testConfig, host))
124 return false;
125 if (options.exact) {
126 const remainingGlobOptions = { ...globOptions, cwd: baselineDir, ignore: baselinesSeen };
127 for (const unchecked of glob.sync('**', remainingGlobOptions)) {
128 if (options.updateBaselines) {
129 this.fs.remove(unchecked);
130 this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.green('REMOVED')}`);
131 }
132 else {
133 this.logger.log(` ${chalk.grey.dim(path.relative(basedir, unchecked))} ${chalk.red('UNCHECKED')}`);
134 if (options.bail)
135 return false;
136 success = false;
137 }
138 }
139 }
140 }
141 }
142 return success;
143 }
144 test(config, host) {
145 const lintOptions = { ...config, fix: false };
146 const lintResult = Array.from(this.runner.lintCollection(lintOptions));
147 let containsFixes = false;
148 for (const [fileName, summary] of lintResult) {
149 if (!host.checkResult(fileName, "lint" /* Lint */, summary))
150 return false;
151 containsFixes = containsFixes || summary.findings.some(isFixable);
152 }
153 if (config.fix || config.fix === undefined) {
154 lintOptions.fix = config.fix || true; // fix defaults to true if not specified
155 const fixResult = containsFixes ? this.runner.lintCollection(lintOptions) : lintResult;
156 for (const [fileName, summary] of fixResult)
157 if (!host.checkResult(fileName, "fix" /* Fix */, summary))
158 return false;
159 }
160 return true;
161 }
162};
163TestCommandRunner = tslib_1.__decorate([
164 inversify_1.injectable(),
165 tslib_1.__metadata("design:paramtypes", [runner_1.Runner,
166 cached_file_system_1.CachedFileSystem,
167 ymir_1.MessageHandler,
168 FakeDirectoryService])
169], TestCommandRunner);
170function buildBaselineDirectoryName(basedir, baselineDir, testcase) {
171 const parts = path.relative(basedir, path.dirname(testcase)).split(path.sep);
172 if (/^(__)?tests?(__)?$/.test(parts[0])) {
173 parts[0] = baselineDir;
174 }
175 else {
176 parts.unshift(baselineDir);
177 }
178 return path.resolve(basedir, parts.join(path.sep), getTestName(path.basename(testcase)));
179}
180function getTestName(basename) {
181 let ext = path.extname(basename);
182 basename = basename.slice(0, -ext.length);
183 ext = path.extname(basename);
184 if (ext === '')
185 return 'default';
186 return basename.slice(0, -ext.length);
187}
188/** Removes everything related to prereleases and just returns MAJOR.MINOR.PATCH, thus treating prereleases like the stable release. */
189function getNormalizedTypescriptVersion() {
190 const v = new semver_1.SemVer(ts.version);
191 return new semver_1.SemVer(`${v.major}.${v.minor}.${v.patch}`);
192}
193function isFixable(finding) {
194 return finding.fix !== undefined;
195}
196function createBaselineDiff(actual, expected) {
197 const result = [
198 chalk.red('Expected'),
199 chalk.green('Actual'),
200 ];
201 const lines = diff.createPatch('', expected, actual, '', '').split(/\n(?!\\)/g).slice(4);
202 for (let line of lines) {
203 switch (line[0]) {
204 case '@':
205 line = chalk.blueBright(line);
206 break;
207 case '+':
208 line = chalk.green('+' + prettyLine(line.substr(1)));
209 break;
210 case '-':
211 line = chalk.red('-' + prettyLine(line.substr(1)));
212 }
213 result.push(line);
214 }
215 return result.join('\n');
216}
217function prettyLine(line) {
218 return line
219 .replace(/\t/g, '\u2409') // ␉
220 .replace(/\r$/, '\u240d') // ␍
221 .replace(/^\uFEFF/, '<BOM>');
222}
223exports.module = new inversify_1.ContainerModule((bind) => {
224 bind(FakeDirectoryService).toSelf().inSingletonScope();
225 bind(ymir_1.DirectoryService).toDynamicValue((context) => {
226 return context.container.get(FakeDirectoryService);
227 }).inSingletonScope().when((request) => {
228 return request.parentRequest == undefined || request.parentRequest.target.serviceIdentifier !== FakeDirectoryService;
229 });
230 bind(ymir_1.DirectoryService).toDynamicValue(({ container }) => {
231 if (container.parent && container.parent.isBound(ymir_1.DirectoryService))
232 return container.parent.get(ymir_1.DirectoryService);
233 return {
234 getCurrentDirectory() {
235 return process.cwd();
236 },
237 };
238 }).inSingletonScope().whenInjectedInto(FakeDirectoryService);
239 bind(base_1.AbstractCommandRunner).to(TestCommandRunner);
240});
241//# sourceMappingURL=test.js.map
\No newline at end of file