@@ -11,9 +11,16 @@ import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
11
11
import * as virtualFs from '@angular-devkit/core/src/virtual-fs/host' ;
12
12
import { SchematicTestRunner } from '@angular-devkit/schematics/testing' ;
13
13
import { mkdirpSync , readFileSync , writeFileSync , removeSync } from 'fs-extra' ;
14
- import { dirname , join } from 'path' ;
14
+ import { sync as globSync } from 'glob' ;
15
+ import { dirname , join , basename , relative , sep } from 'path' ;
15
16
import { createTestApp , runPostScheduledTasks } from '../testing' ;
16
17
18
+ /** Suffix that indicates whether a given file is a test case input. */
19
+ const TEST_CASE_INPUT_SUFFIX = '_input.ts' ;
20
+
21
+ /** Suffix that indicates whether a given file is an expected output of a test case. */
22
+ const TEST_CASE_OUTPUT_SUFFIX = '_expected_output.ts' ;
23
+
17
24
/** Reads the UTF8 content of the specified file. Normalizes the path and ensures that */
18
25
export function readFileContent ( filePath : string ) : string {
19
26
return readFileSync ( filePath , 'utf8' ) ;
@@ -38,10 +45,9 @@ export function createFileSystemTestApp(runner: SchematicTestRunner) {
38
45
}
39
46
40
47
export async function runTestCases ( migrationName : string , collectionPath : string ,
41
- inputs : { [ name : string ] : string } ) {
48
+ inputFiles : string [ ] ) {
42
49
43
50
const runner = new SchematicTestRunner ( 'schematics' , collectionPath ) ;
44
- const inputNames = Object . keys ( inputs ) ;
45
51
const initialWorkingDir = process . cwd ( ) ;
46
52
47
53
let logOutput = '' ;
@@ -51,11 +57,12 @@ export async function runTestCases(migrationName: string, collectionPath: string
51
57
52
58
// Write each test-case input to the file-system. This is necessary because otherwise
53
59
// TSLint won't be able to pick up the test cases.
54
- inputNames . forEach ( inputName => {
55
- const tempInputPath = join ( tempPath , `projects/cdk-testing/src/test-cases/${ inputName } .ts` ) ;
60
+ inputFiles . forEach ( inputFilePath => {
61
+ const inputTestName = basename ( inputFilePath ) ;
62
+ const tempInputPath = join ( tempPath , `projects/cdk-testing/src/test-cases/${ inputTestName } .ts` ) ;
56
63
57
64
mkdirpSync ( dirname ( tempInputPath ) ) ;
58
- writeFileSync ( tempInputPath , readFileContent ( inputs [ inputName ] ) ) ;
65
+ writeFileSync ( tempInputPath , readFileContent ( inputFilePath ) ) ;
59
66
} ) ;
60
67
61
68
runner . runSchematic ( migrationName , { } , appTree ) ;
@@ -73,3 +80,87 @@ export async function runTestCases(migrationName: string, collectionPath: string
73
80
74
81
return { tempPath, logOutput, removeTempDir} ;
75
82
}
83
+
84
+ /**
85
+ * Resolves all test cases for specified path using Bazel's runfile manifest. Note that we
86
+ * cannot just use "glob" since the test case files are not copied to the Bazel bin directory
87
+ * and are just runfiles.
88
+ */
89
+ export function findBazelVersionTestCases ( basePath : string ) {
90
+ const testCasesMap = new Map < string , string [ ] > ( ) ;
91
+ const manifestPath = process . env [ 'RUNFILES_MANIFEST_FILE' ] ! ;
92
+ const runfilesDir = process . env [ 'RUNFILES' ] ;
93
+
94
+ // In case we are not on Windows where runfiles are symlinked, we just find all
95
+ // test case files by using "glob" and store them in our result map.
96
+ if ( ! manifestPath ) {
97
+ const runfilesBaseDir = join ( runfilesDir , basePath ) ;
98
+ const inputFiles = globSync ( `**/*${ TEST_CASE_INPUT_SUFFIX } ` , { cwd : runfilesBaseDir } ) ;
99
+
100
+ inputFiles . forEach ( inputFile => {
101
+ // The target version of an input file will be determined from the first
102
+ // path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
103
+ const targetVersion = inputFile . split ( sep ) [ 0 ] ;
104
+ const resolvedInputPath = join ( runfilesBaseDir , inputFile ) ;
105
+
106
+ testCasesMap . set ( targetVersion ,
107
+ ( testCasesMap . get ( targetVersion ) || [ ] ) . concat ( resolvedInputPath ) ) ;
108
+ } ) ;
109
+
110
+ return testCasesMap ;
111
+ }
112
+
113
+ // In case runfiles are not symlinked (e.g. on Windows), we resolve all test case files using
114
+ // the Bazel runfiles manifest. Read more about the manifest here: https://git.io/fhIZE
115
+ readFileSync ( manifestPath , 'utf8' ) . split ( '\n' ) . forEach ( line => {
116
+ const [ runfilePath , realPath ] = line . split ( ' ' ) ;
117
+
118
+ // In case the mapped runfile starts with the specified base path and ends with "_input.ts",
119
+ // we store it in our result map because we assume that this is a test case.
120
+ if ( runfilePath . startsWith ( basePath ) && runfilePath . endsWith ( TEST_CASE_INPUT_SUFFIX ) ) {
121
+ // The target version of an input file will be determined from the first
122
+ // path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
123
+ const targetVersion = relative ( basePath , runfilePath ) . split ( sep ) [ 0 ] ;
124
+ testCasesMap . set ( targetVersion , ( testCasesMap . get ( targetVersion ) || [ ] ) . concat ( realPath ) ) ;
125
+ }
126
+ } ) ;
127
+
128
+ return testCasesMap ;
129
+ }
130
+
131
+ /**
132
+ * Sets up the specified test cases using Jasmine by creating the appropriate jasmine
133
+ * spec definitions. This should be used within a "describe" jasmine closure.
134
+ */
135
+ export function defineJasmineTestCases ( versionName : string , collectionFile : string ,
136
+ inputFiles : string [ ] | undefined ) {
137
+ // No test cases for the given version are available. Skip setting up tests for that
138
+ // version.
139
+ if ( ! inputFiles ) {
140
+ return ;
141
+ }
142
+
143
+ let testCasesOutputPath : string ;
144
+ let cleanupTestApp : ( ) => void ;
145
+
146
+ beforeAll ( async ( ) => {
147
+ const { tempPath, removeTempDir} =
148
+ await runTestCases ( `migration-${ versionName } ` , collectionFile , inputFiles ) ;
149
+
150
+ testCasesOutputPath = join ( tempPath , 'projects/cdk-testing/src/test-cases/' ) ;
151
+ cleanupTestApp = removeTempDir ;
152
+ } ) ;
153
+
154
+ afterAll ( ( ) => cleanupTestApp ( ) ) ;
155
+
156
+ // Iterates through every test case directory and generates a jasmine test block that will
157
+ // verify that the update schematics properly updated the test input to the expected output.
158
+ inputFiles . forEach ( inputFile => {
159
+ const inputTestName = basename ( inputFile ) ;
160
+
161
+ it ( `should apply update schematics to test case: ${ inputTestName } ` , ( ) => {
162
+ expect ( readFileContent ( join ( testCasesOutputPath , `${ inputTestName } .ts` ) ) )
163
+ . toBe ( readFileContent ( inputFile . replace ( TEST_CASE_INPUT_SUFFIX , TEST_CASE_OUTPUT_SUFFIX ) ) ) ;
164
+ } ) ;
165
+ } ) ;
166
+ }
0 commit comments