-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(angular): switch to using jasmine-marbles for certain symbols (#1…
- Loading branch information
Showing
7 changed files
with
320 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,6 @@ | |
"chalk", | ||
"chokidar", | ||
"ignore", | ||
"jasmine-marbles", | ||
"minimatch", | ||
"rxjs-for-await", | ||
"webpack-merge", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
packages/angular/src/migrations/update-15-0-0/switch-to-jasmine-marbles.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import switchToJasmineMarbles from './switch-to-jasmine-marbles'; | ||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; | ||
import { | ||
addProjectConfiguration, | ||
DependencyType, | ||
ProjectGraph, | ||
readJson, | ||
} from '@nrwl/devkit'; | ||
import { jasmineMarblesVersion } from '../../utils/versions'; | ||
|
||
let projectGraph: ProjectGraph; | ||
jest.mock('@nrwl/devkit', () => ({ | ||
...jest.requireActual<any>('@nrwl/devkit'), | ||
readCachedProjectGraph: jest.fn().mockImplementation(() => projectGraph), | ||
createProjectGraphAsync: jest | ||
.fn() | ||
.mockImplementation(async () => projectGraph), | ||
})); | ||
|
||
describe('switchToJasmineMarbles', () => { | ||
it('should correctly migrate a file that is using imports from nrwl/angular/testing that exist in jasmine-marbles', async () => { | ||
// ARRANGE | ||
const tree = createTreeWithEmptyWorkspace(); | ||
|
||
projectGraph = { | ||
nodes: {}, | ||
dependencies: { | ||
test: [ | ||
{ | ||
type: DependencyType.static, | ||
source: 'test', | ||
target: 'npm:@nrwl/angular', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
addProjectConfiguration(tree, 'test', { | ||
name: 'test', | ||
root: '', | ||
}); | ||
|
||
tree.write( | ||
'test/a/b/mytest.spec.ts', | ||
`import {hot, cold} from '@nrwl/angular/testing';` | ||
); | ||
tree.write( | ||
'test/c/d/mytest.spec.ts', | ||
`import {hot, getTestScheduler} from '@nrwl/angular/testing';` | ||
); | ||
tree.write( | ||
'test/e/mytest.spec.ts', | ||
`import {getTestScheduler, time} from '@nrwl/angular/testing';` | ||
); | ||
|
||
// ACT | ||
await switchToJasmineMarbles(tree); | ||
|
||
// ASSERT | ||
expect(tree.read('test/a/b/mytest.spec.ts', 'utf-8')) | ||
.toMatchInlineSnapshot(` | ||
" | ||
import {hot,cold} from 'jasmine-marbles';" | ||
`); | ||
expect(tree.read('test/c/d/mytest.spec.ts', 'utf-8')) | ||
.toMatchInlineSnapshot(` | ||
" | ||
import {hot,getTestScheduler} from 'jasmine-marbles';" | ||
`); | ||
expect(tree.read('test/e/mytest.spec.ts', 'utf-8')).toMatchInlineSnapshot(` | ||
" | ||
import {getTestScheduler,time} from 'jasmine-marbles';" | ||
`); | ||
}); | ||
|
||
it('should correctly migrate and split imports from nrwl/angular/testing that exist in jasmine-marbles and nrwl/angular/testing', async () => { | ||
// ARRANGE | ||
const tree = createTreeWithEmptyWorkspace(); | ||
projectGraph = { | ||
nodes: {}, | ||
dependencies: { | ||
test: [ | ||
{ | ||
type: DependencyType.static, | ||
source: 'test', | ||
target: 'npm:@nrwl/angular', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
addProjectConfiguration(tree, 'test', { | ||
name: 'test', | ||
root: '', | ||
}); | ||
tree.write( | ||
'a/b/mytest.spec.ts', | ||
`import {hot, cold, readFirst} from '@nrwl/angular/testing';` | ||
); | ||
tree.write( | ||
'c/d/mytest.spec.ts', | ||
`import {hot, getTestScheduler, readAll} from '@nrwl/angular/testing';` | ||
); | ||
tree.write( | ||
'e/mytest.spec.ts', | ||
`import {getTestScheduler, time, readAll, readFirst} from '@nrwl/angular/testing';` | ||
); | ||
|
||
// ACT | ||
await switchToJasmineMarbles(tree); | ||
|
||
// ASSERT | ||
expect(tree.read('a/b/mytest.spec.ts', 'utf-8')).toMatchInlineSnapshot(` | ||
"import {readFirst} from '@nrwl/angular/testing'; | ||
import {hot,cold} from 'jasmine-marbles';" | ||
`); | ||
expect(tree.read('c/d/mytest.spec.ts', 'utf-8')).toMatchInlineSnapshot(` | ||
"import {readAll} from '@nrwl/angular/testing'; | ||
import {hot,getTestScheduler} from 'jasmine-marbles';" | ||
`); | ||
expect(tree.read('e/mytest.spec.ts', 'utf-8')).toMatchInlineSnapshot(` | ||
"import {readAll,readFirst} from '@nrwl/angular/testing'; | ||
import {getTestScheduler,time} from 'jasmine-marbles';" | ||
`); | ||
}); | ||
|
||
it('should add jasmine-marbles as a dependency if it does not exist but uses jasmine-marbles symbols in files', async () => { | ||
// ARRANGE | ||
const tree = createTreeWithEmptyWorkspace(); | ||
projectGraph = { | ||
nodes: {}, | ||
dependencies: { | ||
test: [ | ||
{ | ||
type: DependencyType.static, | ||
source: 'test', | ||
target: 'npm:@nrwl/angular', | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
addProjectConfiguration(tree, 'test', { | ||
name: 'test', | ||
root: '', | ||
}); | ||
tree.write( | ||
'a/b/mytest.spec.ts', | ||
`import {hot, cold, readFirst} from '@nrwl/angular/testing';` | ||
); | ||
|
||
// ACT | ||
await switchToJasmineMarbles(tree); | ||
|
||
// ASSERT | ||
|
||
const jasmineMarblesDependency = readJson(tree, 'package.json') | ||
.devDependencies['jasmine-marbles']; | ||
expect(jasmineMarblesDependency).toBeTruthy(); | ||
expect(jasmineMarblesDependency).toBe(jasmineMarblesVersion); | ||
}); | ||
}); |
151 changes: 151 additions & 0 deletions
151
packages/angular/src/migrations/update-15-0-0/switch-to-jasmine-marbles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import type { Tree } from '@nrwl/devkit'; | ||
import { | ||
addDependenciesToPackageJson, | ||
createProjectGraphAsync, | ||
readCachedProjectGraph, | ||
readJson, | ||
readProjectConfiguration, | ||
visitNotIgnoredFiles, | ||
} from '@nrwl/devkit'; | ||
import { extname } from 'path'; | ||
import { tsquery } from '@phenomnomnominal/tsquery'; | ||
import { jasmineMarblesVersion } from '@nrwl/angular/src/utils/versions'; | ||
|
||
export default async function switchToJasmineMarbles(tree: Tree) { | ||
const usesJasmineMarbles = await replaceJasmineMarbleUsagesInFiles(tree); | ||
addJasmineMarblesDevDependencyIfUsed(tree, usesJasmineMarbles); | ||
} | ||
|
||
async function replaceJasmineMarbleUsagesInFiles(tree: Tree) { | ||
let usesJasmineMarbles = false; | ||
|
||
const projectGraph = await (() => { | ||
try { | ||
return readCachedProjectGraph(); | ||
} catch { | ||
return createProjectGraphAsync(); | ||
} | ||
})(); | ||
|
||
const dirsToTraverse = Object.entries(projectGraph.dependencies) | ||
.filter(([, dep]) => | ||
dep.some(({ target }) => target === 'npm:@nrwl/angular') | ||
) | ||
.map(([projectName]) => readProjectConfiguration(tree, projectName).root); | ||
|
||
for (const dir of dirsToTraverse) { | ||
visitNotIgnoredFiles(tree, dir, (path) => { | ||
if (extname(path) !== '.ts') { | ||
return; | ||
} | ||
|
||
const fileContents = tree.read(path, 'utf-8'); | ||
if (!fileContents.includes('@nrwl/angular/testing')) { | ||
return; | ||
} | ||
|
||
const NRWL_ANGULAR_TESTING_IMPORT_SELECTOR = | ||
'ImportDeclaration:has(StringLiteral[value="@nrwl/angular/testing"])'; | ||
const ast = tsquery.ast(fileContents); | ||
const nrwlAngularTestingImportNodes = tsquery( | ||
ast, | ||
NRWL_ANGULAR_TESTING_IMPORT_SELECTOR, | ||
{ visitAllChildren: true } | ||
); | ||
|
||
if ( | ||
!nrwlAngularTestingImportNodes || | ||
nrwlAngularTestingImportNodes.length === 0 | ||
) { | ||
return; | ||
} | ||
|
||
const jasmineMarblesExportsRegex = new RegExp( | ||
/(hot|cold|getTestScheduler|time)/ | ||
); | ||
if ( | ||
!jasmineMarblesExportsRegex.test( | ||
nrwlAngularTestingImportNodes[0].getText() | ||
) | ||
) { | ||
return; | ||
} | ||
|
||
const IMPORT_SPECIFIERS_SELECTOR = 'NamedImports > ImportSpecifier'; | ||
const importSpecifierNodes = tsquery( | ||
nrwlAngularTestingImportNodes[0], | ||
IMPORT_SPECIFIERS_SELECTOR, | ||
{ visitAllChildren: true } | ||
); | ||
|
||
if (!importSpecifierNodes || importSpecifierNodes.length === 0) { | ||
return; | ||
} | ||
|
||
const validNrwlTestingImports = []; | ||
const validJasmineMarbleImports = []; | ||
for (const node of importSpecifierNodes) { | ||
const importSymbol = node.getText(); | ||
if (jasmineMarblesExportsRegex.test(importSymbol)) { | ||
validJasmineMarbleImports.push(importSymbol); | ||
} else { | ||
validNrwlTestingImports.push(importSymbol); | ||
} | ||
} | ||
|
||
if (!usesJasmineMarbles && validJasmineMarbleImports.length > 0) { | ||
usesJasmineMarbles = true; | ||
} | ||
|
||
const newFileContents = `${fileContents.slice( | ||
0, | ||
nrwlAngularTestingImportNodes[0].getStart() | ||
)}${ | ||
validNrwlTestingImports.length > 0 | ||
? `import {${validNrwlTestingImports.join( | ||
',' | ||
)}} from '@nrwl/angular/testing';` | ||
: '' | ||
} | ||
${ | ||
validJasmineMarbleImports.length > 0 | ||
? `import {${validJasmineMarbleImports.join( | ||
',' | ||
)}} from 'jasmine-marbles';${fileContents.slice( | ||
nrwlAngularTestingImportNodes[0].getEnd(), | ||
-1 | ||
)}` | ||
: '' | ||
}`; | ||
|
||
tree.write(path, newFileContents); | ||
}); | ||
} | ||
return usesJasmineMarbles; | ||
} | ||
|
||
function addJasmineMarblesDevDependencyIfUsed( | ||
tree: Tree, | ||
usesJasmineMarbles: boolean | ||
) { | ||
if (!usesJasmineMarbles) { | ||
return; | ||
} | ||
|
||
const pkgJson = readJson(tree, 'package.json'); | ||
const jasmineMarblesDependency = pkgJson.dependencies['jasmine-marbles']; | ||
const jasmineMarblesDevDependency = | ||
pkgJson.devDependencies['jasmine-marbles']; | ||
|
||
if (jasmineMarblesDependency || jasmineMarblesDevDependency) { | ||
return; | ||
} | ||
|
||
addDependenciesToPackageJson( | ||
tree, | ||
{}, | ||
{ | ||
'jasmine-marbles': jasmineMarblesVersion, | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1 @@ | ||
import { | ||
cold as rxjsMarblesCold, | ||
hot as rxjsMarblesHot, | ||
getTestScheduler as rxjsMarblesTestScheduler, | ||
time as rxjsMarblesTime, | ||
} from 'jasmine-marbles'; | ||
|
||
/** | ||
* @deprecated Import from 'jasmine-marbles' instead. Will be removed in Nx v15. | ||
*/ | ||
export const cold = rxjsMarblesCold; | ||
/** | ||
* @deprecated Import from 'jasmine-marbles' instead. Will be removed in Nx v15. | ||
*/ | ||
export const hot = rxjsMarblesHot; | ||
/** | ||
* @deprecated Import from 'jasmine-marbles' instead. Will be removed in Nx v15. | ||
*/ | ||
export const getTestScheduler = rxjsMarblesTestScheduler; | ||
/** | ||
* @deprecated Import from 'jasmine-marbles' instead. Will be removed in Nx v15. | ||
*/ | ||
export const time = rxjsMarblesTime; | ||
|
||
export { readAll, readFirst } from './src/testing-utils'; |