Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(migrations): enable lang service migrations #14812

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion projects/igniteui-angular/migrations/common/ServerHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class ServerHost implements ts.server.ServerHost {
public readonly newLine: string;
public readonly useCaseSensitiveFileNames: boolean;

constructor(private host: Tree) {
constructor(public host: Tree) {
this.args = ts.sys.args;
this.newLine = ts.sys.newLine;
this.useCaseSensitiveFileNames = ts.sys.useCaseSensitiveFileNames;
Expand Down
36 changes: 28 additions & 8 deletions projects/igniteui-angular/migrations/common/UpdateChanges.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EmptyTree } from '@angular-devkit/schematics';
import { UnitTestTree } from '@angular-devkit/schematics/testing';
import * as fs from 'fs';
import * as path from 'path';
import { ClassChanges, BindingChanges, SelectorChanges, ThemeChanges, ImportsChanges, ElementType, ThemeType } from './schema';
import { ClassChanges, BindingChanges, SelectorChanges, ThemeChanges, ImportsChanges, ElementType, ThemeType, MemberChanges } from './schema';
import { UpdateChanges, InputPropertyType, BoundPropertyObject } from './UpdateChanges';
import * as tsUtils from './tsUtils';

Expand Down Expand Up @@ -35,8 +35,14 @@ describe('UpdateChanges', () => {
appTree.create('/angular.json', JSON.stringify({
projects: {
testProj: {
root: '',
sourceRoot: '/'
}
},
schematics: {
'@schematics/angular:component': {
prefix: 'app'
}
}
}));
});
Expand Down Expand Up @@ -915,31 +921,45 @@ export class AppModule { }`);
describe('Language Service migrations', () => {

it('Should be able to replace property of an event', () => {
pending('set up tests for migrations through lang service');
const selectorsJson: MemberChanges = {
changes: [
{ member: 'onGridKeydown', replaceWith: 'gridKeydown', definedIn: ['IgxGridComponent'] }
]
};
const jsonPath = path.join(__dirname, 'changes', 'members.json');

// leave callThrough on spies for other files the LS test might want to load:
spyOn(fs, 'existsSync').and.callThrough()
.withArgs(jsonPath).and.returnValue(true);
spyOn(fs, 'readFileSync').and.callThrough()
.withArgs(jsonPath, jasmine.any(String)).and.returnValue(JSON.stringify(selectorsJson));

const fileContent =
`import { Component } from '@angular/core';
import { IGridCreatedEventArgs } from 'igniteui-angular';
import { IgxGridComponent, IGridKeydownEventArgs } from 'igniteui-angular';
@Component({
selector: 'app-custom-grid',
template: ''
})
export class CustomGridComponent {
public childGridCreated(event: IGridCreatedEventArgs) {
event.grid.onGridKeydown.subscribe(() => {});
public childGridCreated(event: IGridKeydownEventArgs) {
const grid = event.owner as IgxGridComponent;
grid.onGridKeydown.subscribe(() => {});
}
}
`;
appTree.create('test.component.ts', fileContent);
const expectedFileContent =
`import { Component } from '@angular/core';
import { IGridCreatedEventArgs } from 'igniteui-angular';
import { IgxGridComponent, IGridKeydownEventArgs } from 'igniteui-angular';
@Component({
selector: 'app-custom-grid',
template: ''
})
export class CustomGridComponent {
public childGridCreated(event: IGridCreatedEventArgs) {
event.grid.gridKeydown.subscribe(() => {});
public childGridCreated(event: IGridKeydownEventArgs) {
const grid = event.owner as IgxGridComponent;
grid.gridKeydown.subscribe(() => {});
}
}
`;
Expand Down
31 changes: 18 additions & 13 deletions projects/igniteui-angular/migrations/common/UpdateChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import {
} from './schema';
import {
getLanguageService, getRenamePositions, getIdentifierPositions,
createProjectService, isMemberIgniteUI, NG_LANG_SERVICE_PACKAGE_NAME, NG_CORE_PACKAGE_NAME, findMatches
isMemberIgniteUI, NG_LANG_SERVICE_PACKAGE_NAME, NG_CORE_PACKAGE_NAME, findMatches
} from './tsUtils';
import {
getProjectPaths, getWorkspace, getProjects, escapeRegExp, replaceMatch,
getPackageManager, canResolvePackage, tryInstallPackage, tryUninstallPackage, getPackageVersion
} from './util';
import { ServerHost } from './ServerHost';
import { serviceContainer } from './service-container';

const TSCONFIG_PATH = 'tsconfig.json';

Expand All @@ -38,7 +39,6 @@ interface AppliedChange {
/* eslint-disable arrow-parens */
export class UpdateChanges {
protected tsconfigPath = TSCONFIG_PATH;
protected _projectService: tss.server.ProjectService;

public _shouldInvokeLS = true;
public get shouldInvokeLS(): boolean {
Expand All @@ -54,40 +54,44 @@ export class UpdateChanges {
}

public get projectService(): tss.server.ProjectService {
if (!this._projectService) {
this._projectService = createProjectService(this.serverHost);
const projectService = serviceContainer.projectService;
if (!serviceContainer.configured) {
// Force Angular service to compile project on initial load w/ configure project
// otherwise if the first compilation occurs on an HTML file the project won't have proper refs
// and no actual angular metadata will be resolved for the rest of the migration

// TODO: this patter/issue might be obsolete; if so, should be safe to return _projectService directly
// TODO: this pattern/issue might be obsolete
const mainRelPath = this.getWorkspaceProjectEntryPath();
if (!mainRelPath) {
return null;
serviceContainer.configured = true;
return projectService;
}

// patch TSConfig so it includes angularOptions.strictTemplates
// ivy ls requires this in order to function properly on templates
this.patchTsConfig();
const mainAbsPath = path.resolve(this._projectService.currentDirectory, mainRelPath);
const scriptInfo = this._projectService.getOrCreateScriptInfoForNormalizedPath(tss.server.toNormalizedPath(mainAbsPath), false);
this._projectService.openClientFile(scriptInfo.fileName);
const mainAbsPath = path.resolve(projectService.currentDirectory, mainRelPath);
const scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(tss.server.toNormalizedPath(mainAbsPath), false);
projectService.openClientFile(scriptInfo.fileName);


try {
const project = this._projectService.findProject(scriptInfo.containingProjects[0].getProjectName());
const project = projectService.findProject(scriptInfo.containingProjects[0].getProjectName());
project.getLanguageService().getSemanticDiagnostics(mainAbsPath);
} catch (err) {
this.context.logger.warn(
"An error occurred during TypeScript project service setup. Some migrations relying on language services might not be applied."
);
}
serviceContainer.configured = true;
}

return this._projectService;
return projectService;
}

protected serverHost: ServerHost;
protected get serverHost(): ServerHost {
return serviceContainer.serverHost;
}
protected workspace: WorkspaceSchema;
protected sourcePaths: string[];
protected classChanges: ClassChanges;
Expand Down Expand Up @@ -176,7 +180,8 @@ export class UpdateChanges {
this.themeChanges = this.loadConfig('theme-changes.json');
this.importsChanges = this.loadConfig('imports.json');
this.membersChanges = this.loadConfig('members.json');
this.serverHost = new ServerHost(this.host);
// update LS server host with the schematics tree host:
this.serverHost.host = this.host;
}

/** Apply configured changes to the Host Tree */
Expand Down
29 changes: 29 additions & 0 deletions projects/igniteui-angular/migrations/common/service-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as tss from 'typescript/lib/tsserverlibrary';
import { createProjectService } from './tsUtils';
import { ServerHost } from './ServerHost';

export class ServiceContainer {
private _serverHost: ServerHost;
private _projectService: tss.server.ProjectService;

/** Indicates additional config adjustments after init have been made */
public configured = false;


public get serverHost(): ServerHost {
if (!this._serverHost) {
this._serverHost = new ServerHost(null);
}
return this._serverHost;
}

public get projectService(): tss.server.ProjectService {
if (!this._projectService) {
this._projectService = createProjectService(this.serverHost);
}

return this._projectService;
}
}

export const serviceContainer = new ServiceContainer();
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { UnitTestTree } from '@angular-devkit/schematics/testing';
import { join } from 'path';
import * as tss from 'typescript/lib/tsserverlibrary';
import { serviceContainer } from './service-container';

/**
* Hook before _all_ tests to ensure newly created .ts files are added to the project service
* or their source cache is updated/invalidated if the name is reused in multiple tests
*/
beforeAll(() => {
const originalCreate = UnitTestTree.prototype.create;
UnitTestTree.prototype.create = function (path: string, content: Buffer | string) {
originalCreate.call(this, path, content);
if (path.endsWith('.ts') && serviceContainer.configured && serviceContainer.projectService.configuredProjects.size) {
// rush host update
serviceContainer.serverHost.host = this;

const entryPath = tss.server.toNormalizedPath(join(process.cwd(), path))
const scriptInfo = serviceContainer.projectService?.getOrCreateScriptInfoForNormalizedPath(entryPath, false);
if (!scriptInfo) {
return;
}
// integrate incoming virtual file in project/program for LS to work:
const project = serviceContainer.projectService.configuredProjects.values().next().value as tss.server.ConfiguredProject;
if (!project.containsScriptInfo(scriptInfo) /* && this.host.exists() */) {
// add first to prevent open from creating another project that'll be inferred
project.addMissingFileRoot(scriptInfo.fileName);
} else {
// if using same file, force-reload from host for new content
scriptInfo.reloadFromFile(tss.server.asNormalizedPath(entryPath));
}
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,7 @@ describe('Update 10.2.0', () => {
).toEqual(`<igx-input-group type="line"></igx-input-group><igx-input-group type="box"></igx-input-group><igx-input-group type="border"></igx-input-group><igx-input-group type="search"></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group><igx-input-group></igx-input-group>`);
});

it('should replace selectedRows() with selectedRows in ts files', async () => {
pending('set up tests for migrations through lang service');
});

it('Should remove references to deprecated `pane` property of `IExpansionPanelEventArgs`', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/expansion-test.component.ts',
`import { Component, ViewChild } from '@angular/core';
Expand Down
22 changes: 6 additions & 16 deletions projects/igniteui-angular/migrations/update-11_1_0/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ describe('Update to 11.1.0', () => {
});

it('should migrate updated getter names', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/icon-test.component.ts',
`import { Component, ViewChild } from '@angular/core';
Expand Down Expand Up @@ -111,9 +110,6 @@ export class IconTestComponent {
imports: [IgxIconModule]
});
`;
console.log(tree.readContent(
'/testSrc/appPrefix/component/icon-test.component.ts'
));

expect(
tree.readContent(
Expand All @@ -123,11 +119,10 @@ export class IconTestComponent {
});

it('should migrate updated members names', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/icon-test.component.ts',
`import { Component } from '@angular/core';
import { IgxIconService } from 'igniteui-angular';
import { IgxIconService } from 'igniteui-angular';

@Component({
selector: 'app-icon-test',
Expand All @@ -152,7 +147,7 @@ export class IconTestComponent {
.runSchematic('migration-19', {}, appTree);

const expectedContent = `import { Component } from '@angular/core';
import { IgxIconService } from 'igniteui-angular';
import { IgxIconService } from 'igniteui-angular';

@Component({
selector: 'app-icon-test',
Expand Down Expand Up @@ -321,7 +316,6 @@ export class IconTestComponent {
});

it('should update Excel exporter onExportEnded event name to exportEnded', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/excel-export.component.ts',
`import { Component } from '@angular/core';
Expand Down Expand Up @@ -378,7 +372,6 @@ export class ExcelExportComponent {
});

it('should update CSV exporter onExportEnded event name to exportEnded', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/csv-export.component.ts',
`import { Component } from '@angular/core';
Expand Down Expand Up @@ -617,7 +610,6 @@ export class CsvExportComponent {
});

it('should update Excel exporter onColumnExport and onRowExport event names to columnmExporting and rowExporting', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/excel-export.component.ts',
`import { Component } from '@angular/core';
Expand Down Expand Up @@ -676,7 +668,6 @@ export class ExcelExportComponent {
});

it('should update CSV exporter onColumnExport and onRowExport event names to columnmExporting and rowExporting', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/csv-export.component.ts',
`import { Component } from '@angular/core';
Expand Down Expand Up @@ -734,7 +725,6 @@ export class CsvExportComponent {
});

it('should update GridPagingMode enum from lowerCase to TitleCase', async () => {
pending('set up tests for migrations through lang service');
appTree.create(
'/testSrc/appPrefix/component/paging-test.component.ts',
`import { Component } from '@angular/core';
Expand All @@ -746,8 +736,8 @@ import { GridPagingMode } from "igniteui-angular";
templateUrl: "./paging-test.component.html"
})
export class PagingComponent {
public pagingLocal: GridPagingMode = GridPagingMode.Local;
public pagingRemote: GridPagingMode = GridPagingMode.Remote;
public pagingLocal: GridPagingMode = GridPagingMode.local;
public pagingRemote: GridPagingMode = GridPagingMode.remote;
constructor(){}
}
`);
Expand All @@ -765,8 +755,8 @@ import { GridPagingMode } from "igniteui-angular";
templateUrl: "./paging-test.component.html"
})
export class PagingComponent {
public pagingLocal: GridPagingMode = GridPagingMode.local;
public pagingRemote: GridPagingMode = GridPagingMode.remote;
public pagingLocal: GridPagingMode = GridPagingMode.Local;
public pagingRemote: GridPagingMode = GridPagingMode.Remote;
constructor(){}
}
`;
Expand Down
Loading
Loading