| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import * as ts from 'typescript/lib/tsserverlibrary'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-16 11:21:24 -07:00
										 |  |  | import {APP_COMPONENT, APP_MAIN, MockService, setup, TEST_SRCDIR} from './mock_host'; | 
					
						
							| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | describe('mock host', () => { | 
					
						
							| 
									
										
										
										
											2020-10-16 11:21:24 -07:00
										 |  |  |   let service: MockService; | 
					
						
							|  |  |  |   let project: ts.server.Project; | 
					
						
							|  |  |  |   let tsLS: ts.LanguageService; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   beforeAll(() => { | 
					
						
							|  |  |  |     const {project: _project, service: _service, tsLS: _tsLS} = setup(); | 
					
						
							|  |  |  |     project = _project; | 
					
						
							|  |  |  |     service = _service; | 
					
						
							|  |  |  |     tsLS = _tsLS; | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-29 15:52:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   beforeEach(() => { | 
					
						
							|  |  |  |     service.reset(); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  |   it('can load test project from Bazel runfiles', () => { | 
					
						
							|  |  |  |     expect(project).toBeInstanceOf(ts.server.ConfiguredProject); | 
					
						
							| 
									
										
										
										
											2020-04-29 15:52:17 -07:00
										 |  |  |     const configPath = (project as ts.server.ConfiguredProject).getConfigFilePath(); | 
					
						
							|  |  |  |     expect(configPath.substring(TEST_SRCDIR.length)) | 
					
						
							|  |  |  |         .toBe('/angular/packages/language-service/test/project/tsconfig.json'); | 
					
						
							| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  |     const program = tsLS.getProgram(); | 
					
						
							|  |  |  |     expect(program).toBeDefined(); | 
					
						
							|  |  |  |     const sourceFiles = program!.getSourceFiles().map(sf => { | 
					
						
							|  |  |  |       const {fileName} = sf; | 
					
						
							|  |  |  |       if (fileName.startsWith(TEST_SRCDIR)) { | 
					
						
							|  |  |  |         return fileName.substring(TEST_SRCDIR.length); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return fileName; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     expect(sourceFiles).toEqual(jasmine.arrayContaining([ | 
					
						
							|  |  |  |       // This shows that module resolution works
 | 
					
						
							|  |  |  |       '/angular/packages/common/src/common.d.ts', | 
					
						
							|  |  |  |       '/angular/packages/core/src/core.d.ts', | 
					
						
							|  |  |  |       '/angular/packages/forms/src/forms.d.ts', | 
					
						
							|  |  |  |       // This shows that project files are present
 | 
					
						
							|  |  |  |       '/angular/packages/language-service/test/project/app/app.component.ts', | 
					
						
							|  |  |  |       '/angular/packages/language-service/test/project/app/main.ts', | 
					
						
							|  |  |  |       '/angular/packages/language-service/test/project/app/parsing-cases.ts', | 
					
						
							|  |  |  |     ])); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('produces no TS error for test project', () => { | 
					
						
							|  |  |  |     const errors = project.getAllProjectErrors(); | 
					
						
							|  |  |  |     expect(errors).toEqual([]); | 
					
						
							|  |  |  |     const globalErrors = project.getGlobalProjectErrors(); | 
					
						
							|  |  |  |     expect(globalErrors).toEqual([]); | 
					
						
							|  |  |  |     const diags = tsLS.getSemanticDiagnostics(APP_MAIN); | 
					
						
							|  |  |  |     expect(diags).toEqual([]); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-29 15:52:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('can overwrite test file', () => { | 
					
						
							|  |  |  |     service.overwrite(APP_MAIN, `const x: string = 0`); | 
					
						
							|  |  |  |     const scriptInfo = service.getScriptInfo(APP_MAIN); | 
					
						
							|  |  |  |     expect(getText(scriptInfo)).toBe('const x: string = 0'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-21 10:35:49 -07:00
										 |  |  |   describe('overwrite()', () => { | 
					
						
							|  |  |  |     it('will return the cursor position', () => { | 
					
						
							|  |  |  |       const {position} = service.overwrite(APP_MAIN, `const fo¦o = 'hello world';`); | 
					
						
							|  |  |  |       expect(position).toBe(8); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will remove the cursor in overwritten text', () => { | 
					
						
							|  |  |  |       const {text} = service.overwrite(APP_MAIN, `const fo¦o = 'hello world';`); | 
					
						
							|  |  |  |       expect(text).toBe(`const foo = 'hello world';`); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will update script info without cursor', () => { | 
					
						
							|  |  |  |       const {text} = service.overwrite(APP_MAIN, `const fo¦o = 'hello world';`); | 
					
						
							|  |  |  |       const scriptInfo = service.getScriptInfo(APP_MAIN); | 
					
						
							|  |  |  |       const snapshot = getText(scriptInfo); | 
					
						
							|  |  |  |       expect(snapshot).toBe(`const foo = 'hello world';`); | 
					
						
							|  |  |  |       expect(snapshot).toBe(text); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will throw if there is more than one cursor', () => { | 
					
						
							|  |  |  |       expect(() => service.overwrite(APP_MAIN, `const f¦oo = 'hello wo¦rld';`)) | 
					
						
							|  |  |  |           .toThrowError(/matches more than one occurrence in text/); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will return -1 if cursor is not present', () => { | 
					
						
							|  |  |  |       const {position} = service.overwrite(APP_MAIN, `const foo = 'hello world';`); | 
					
						
							|  |  |  |       expect(position).toBe(-1); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('overwriteInlineTemplate()', () => { | 
					
						
							|  |  |  |     it('will return the cursor position', () => { | 
					
						
							|  |  |  |       const {position, text} = service.overwriteInlineTemplate(APP_COMPONENT, `{{ fo¦o }}`); | 
					
						
							|  |  |  |       // The position returned should be relative to the start of the source
 | 
					
						
							|  |  |  |       // file, not the start of the template.
 | 
					
						
							|  |  |  |       expect(position).not.toBe(5); | 
					
						
							|  |  |  |       expect(text.substring(position, position + 4)).toBe('o }}'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will remove the cursor in overwritten text', () => { | 
					
						
							|  |  |  |       const {text} = service.overwriteInlineTemplate(APP_COMPONENT, `{{ fo¦o }}`); | 
					
						
							|  |  |  |       expect(text).toContain(`{{ foo }}`); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will return the entire content of the source file', () => { | 
					
						
							|  |  |  |       const {text} = service.overwriteInlineTemplate(APP_COMPONENT, `{{ foo }}`); | 
					
						
							|  |  |  |       expect(text).toContain(`@Component`); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will update script info without cursor', () => { | 
					
						
							|  |  |  |       service.overwriteInlineTemplate(APP_COMPONENT, `{{ fo¦o }}`); | 
					
						
							|  |  |  |       const scriptInfo = service.getScriptInfo(APP_COMPONENT); | 
					
						
							|  |  |  |       expect(getText(scriptInfo)).toContain(`{{ foo }}`); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will throw if there is no template in file', () => { | 
					
						
							|  |  |  |       expect(() => service.overwriteInlineTemplate(APP_MAIN, `{{ foo }}`)) | 
					
						
							|  |  |  |           .toThrowError(/does not contain a component with template/); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will throw if there is more than one cursor', () => { | 
					
						
							|  |  |  |       expect(() => service.overwriteInlineTemplate(APP_COMPONENT, `{{ f¦o¦o }}`)) | 
					
						
							|  |  |  |           .toThrowError(/matches more than one occurrence in text/); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will return -1 if cursor is not present', () => { | 
					
						
							|  |  |  |       const {position} = service.overwriteInlineTemplate(APP_COMPONENT, `{{ foo }}`); | 
					
						
							|  |  |  |       expect(position).toBe(-1); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('will throw if there is more than one component with template', () => { | 
					
						
							|  |  |  |       service.overwrite(APP_COMPONENT, `
 | 
					
						
							|  |  |  |         import {Component} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           template: \`<h1></h1>\`,
 | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class ComponentA {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         @Component({ | 
					
						
							|  |  |  |           template: \`<h2></h2>\`,
 | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         export class ComponentB {} | 
					
						
							|  |  |  |       `);
 | 
					
						
							|  |  |  |       expect(() => service.overwriteInlineTemplate(APP_COMPONENT, `<p></p>`)) | 
					
						
							|  |  |  |           .toThrowError(/matches more than one occurrence in text/); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-04-29 15:52:17 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-04-30 15:48:20 -07:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2020-04-29 15:52:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function getText(scriptInfo: ts.server.ScriptInfo): string { | 
					
						
							|  |  |  |   const snapshot = scriptInfo.getSnapshot(); | 
					
						
							|  |  |  |   return snapshot.getText(0, snapshot.getLength()); | 
					
						
							|  |  |  | } |