From d3dd338e4648786670092e980f6114b04b382b1c Mon Sep 17 00:00:00 2001 From: smoser-LiL Date: Tue, 20 Jul 2021 21:14:58 +0000 Subject: [PATCH 1/5] Moving files using main --- NOTICE | 30 ++++++++++++++++++++++++++++-- README.md | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/NOTICE b/NOTICE index 6ebd80d..074572e 100644 --- a/NOTICE +++ b/NOTICE @@ -1,11 +1,15 @@ -Copyright 2019 LinkedIn Corporation +Copyright 2021 LinkedIn Corporation All Rights Reserved. Licensed under the LinkedIn Learning Exercise File License (the "License"). See LICENSE in the project root for license information. ATTRIBUTIONS: -[PLEASE PROVIDE ATTRIBUTIONS OR DELETE THIS AND THE ABOVE LINE “ATTRIBUTIONS”] +Angular +https://github.com/angular/angular +Copyright (c) 2010-2021 Google LLC. https://angular.io/license +License: MIT +https://opensource.org/licenses/MIT Please note, this project may automatically load third party code from external repositories (for example, NPM modules, Composer packages, or other dependencies). @@ -13,3 +17,25 @@ If so, such third party code may be subject to other license terms than as set forth above. In addition, such third party code may also depend on and load multiple tiers of dependencies. Please review the applicable licenses of the additional dependencies. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b42fd25..b855317 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# COURSENAME -This is the repository for the LinkedIn Learning course `course-name`. The full course is available from [LinkedIn Learning][lil-course-url]. +# Angular: Testing and Debugging +This is the repository for the LinkedIn Learning course Angular: Testing and Debugging. The full course is available from [LinkedIn Learning][lil-course-url]. -![course-name-alt-text][lil-thumbnail-url] +![Angular: Testing and Debugging][lil-thumbnail-url] +Errors caused by missing dependencies, undefined variables, or poorly formatted data can cause your web application to stop working. In this course, learn how to track down and eliminate these types of errors in your Angular applications through the process of testing and debugging. Instructor Derek Peruo breaks down common error messages and goes over a variety of testing and debugging options. He also covers code linting, shares some tips and tricks for avoiding pitfalls during development, and goes into writing custom error handlers to make it easier to work with errors as your application grows. -_See the readme file in the main branch for updated instructions and information._ ## Instructions This repository has branches for each of the videos in the course. You can use the branch pop up menu in github to switch to a specific branch and take a look at the course at that stage, or you can add `/tree/BRANCH_NAME` to the URL to go to the branch you want to access. @@ -11,15 +11,34 @@ This repository has branches for each of the videos in the course. You can use t The branches are structured to correspond to the videos in the course. The naming convention is `CHAPTER#_MOVIE#`. As an example, the branch named `02_03` corresponds to the second chapter and the third video in that chapter. Some branches will have a beginning and an end state. These are marked with the letters `b` for "beginning" and `e` for "end". The `b` branch contains the code as it is at the beginning of the movie. The `e` branch contains the code as it is at the end of the movie. The `main` branch holds the final state of the code when in the course. +When switching from one exercise files branch to the next after making changes to the files, you may get a message like this: + + error: Your local changes to the following files would be overwritten by checkout: [files] + Please commit your changes or stash them before you switch branches. + Aborting + +To resolve this issue: + + Add changes to git using this command: git add . + Commit changes using this command: git commit -m "some message" + ## Installing 1. To use these exercise files, you must have the following installed: - - [list of requirements for course] -2. Clone this repository into your local machine using the terminal (Mac), CMD (Windows), or a GUI tool like SourceTree. -3. [Course-specific instructions] + - [Node and npm](https://nodejs.org/) + - [Visual Studio Code](https://code.visualstudio.com/) (optional - use any editor you like) + - [Google Chrome](https://www.google.com/chrome/) (optional - use any browser you like) + - Mac, Windows, Linux +2. Clone this repository into your local machine using the terminal (Mac), CMD (Windows), or a GUI tool like [SourceTree](https://www.sourcetreeapp.com/). +3. Run `npm install` from the `main` branch to install project dependencies. + +### Instructor + +**Derek Peruo** +_Angular.js Architect_ -[0]: # (Replace these placeholder URLs with actual course URLs) +Check out my other courses on [LinkedIn Learning](https://www.linkedin.com/learning/instructors/derek-peruo?u=104). -[lil-course-url]: https://www.linkedin.com/learning/ -[lil-thumbnail-url]: http:// +[lil-course-url]: https://www.linkedin.com/learning/angular-testing-and-debugging-10201318 +[lil-thumbnail-url]: https://cdn.lynda.com/course/2875342/2875342-1619631339971-16x9.jpg From e02a8de67e821c84eba948267e1f531612c3b826 Mon Sep 17 00:00:00 2001 From: Anoop Pemmaraju Date: Mon, 2 Feb 2026 22:34:06 +0530 Subject: [PATCH 2/5] Web storage initial commit --- src/app/services/web-storage.service.spec.ts | 16 ++++++++++++++++ src/app/services/web-storage.service.ts | 16 ++++++++++++++++ src/app/user-list/user-list.component.ts | 8 +++----- 3 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 src/app/services/web-storage.service.spec.ts create mode 100644 src/app/services/web-storage.service.ts diff --git a/src/app/services/web-storage.service.spec.ts b/src/app/services/web-storage.service.spec.ts new file mode 100644 index 0000000..78ea4d8 --- /dev/null +++ b/src/app/services/web-storage.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { WebStorageService } from './web-storage.service'; + +describe('WebStorageService', () => { + let service: WebStorageService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(WebStorageService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/web-storage.service.ts b/src/app/services/web-storage.service.ts new file mode 100644 index 0000000..27b55cc --- /dev/null +++ b/src/app/services/web-storage.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class WebStorageService { + constructor() {} + + public get(key: string): string | null { + return window.localStorage.getItem(key); + } + + public set(key: string, value: string): void { + window.localStorage.setItem(key, value); + } +} diff --git a/src/app/user-list/user-list.component.ts b/src/app/user-list/user-list.component.ts index 8854c5d..0a0ddd7 100644 --- a/src/app/user-list/user-list.component.ts +++ b/src/app/user-list/user-list.component.ts @@ -1,18 +1,17 @@ import { Component, OnInit } from '@angular/core'; import { User } from '../user/user'; import { UserListService } from './user-list.service'; +import { WebStorageService } from '../services/web-storage.service'; @Component({ selector: 'app-user-list', templateUrl: './user-list.component.html', - styleUrls: ['./user-list.component.css'] + styleUrls: ['./user-list.component.css'], }) export class UserListComponent implements OnInit { public users: User[] | null = null; - constructor( - // private userListService: UserListService - ) { } + constructor(private userListService: UserListService) {} public async ngOnInit(): Promise { this.users = await this.userListService.getAll(); @@ -21,5 +20,4 @@ export class UserListComponent implements OnInit { public async update(text: string): Promise { this.users = await this.userListService.filter(text); } - } From e7d18dd243f497916b4c1a8344254f7634292b98 Mon Sep 17 00:00:00 2001 From: Anoop Pemmaraju Date: Fri, 6 Feb 2026 22:20:18 +0530 Subject: [PATCH 3/5] Angular Debugging and Filter Commits --- src/app/app.module.ts | 6 +++++- src/app/pipes/filter-locationid.pipe.spec.ts | 8 +++++++ src/app/pipes/filter-locationid.pipe.ts | 15 +++++++++++++ src/app/pipes/highlight-text.pipe.spec.ts | 8 +++++++ src/app/pipes/highlight-text.pipe.ts | 17 +++++++++++++++ src/app/user-list/user-list.component.html | 22 +++++++++++++++++--- src/app/user-list/user-list.component.ts | 12 +++++++++-- src/styles.css | 4 ++++ 8 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 src/app/pipes/filter-locationid.pipe.spec.ts create mode 100644 src/app/pipes/filter-locationid.pipe.ts create mode 100644 src/app/pipes/highlight-text.pipe.spec.ts create mode 100644 src/app/pipes/highlight-text.pipe.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b0934fb..f4a576a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,11 +5,15 @@ import { AppComponent } from './app.component'; import { UserListComponent } from './user-list/user-list.component'; import { ErrorMetadataService } from './services/error-metadata.service'; +import { HighlightTextPipe } from './pipes/highlight-text.pipe'; +import { FilterLocationidPipe } from './pipes/filter-locationid.pipe'; @NgModule({ declarations: [ AppComponent, - UserListComponent + UserListComponent, + HighlightTextPipe, + FilterLocationidPipe ], imports: [ BrowserModule, diff --git a/src/app/pipes/filter-locationid.pipe.spec.ts b/src/app/pipes/filter-locationid.pipe.spec.ts new file mode 100644 index 0000000..aa72284 --- /dev/null +++ b/src/app/pipes/filter-locationid.pipe.spec.ts @@ -0,0 +1,8 @@ +import { FilterLocationidPipe } from './filter-locationid.pipe'; + +describe('FilterLocationidPipe', () => { + it('create an instance', () => { + const pipe = new FilterLocationidPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/filter-locationid.pipe.ts b/src/app/pipes/filter-locationid.pipe.ts new file mode 100644 index 0000000..7342748 --- /dev/null +++ b/src/app/pipes/filter-locationid.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { User } from '../user/user'; + +@Pipe({ + name: 'filterLocationid', +}) +export class FilterLocationidPipe implements PipeTransform { + transform(users: User[] | null, ...ids: number[]): User[] | null { + if (users === null || ids.length === 0) { + return users; + } + + return users.filter((user) => ids.some((id) => user.locationId === id)); + } +} diff --git a/src/app/pipes/highlight-text.pipe.spec.ts b/src/app/pipes/highlight-text.pipe.spec.ts new file mode 100644 index 0000000..6e508c1 --- /dev/null +++ b/src/app/pipes/highlight-text.pipe.spec.ts @@ -0,0 +1,8 @@ +import { HighlightTextPipe } from './highlight-text.pipe'; + +describe('HighlightTextPipe', () => { + it('create an instance', () => { + const pipe = new HighlightTextPipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src/app/pipes/highlight-text.pipe.ts b/src/app/pipes/highlight-text.pipe.ts new file mode 100644 index 0000000..1f3cd6e --- /dev/null +++ b/src/app/pipes/highlight-text.pipe.ts @@ -0,0 +1,17 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'highlightText', +}) +export class HighlightTextPipe implements PipeTransform { + transform(value: string, filter: string): string { + if (filter.length === 0) return value; + + const search = new RegExp(filter, 'ig'); //i => ignore case, g => global + return value.replace(search, (match) => { + return `${match}`; + }); + } +} + +//cross site scripting, angular security diff --git a/src/app/user-list/user-list.component.html b/src/app/user-list/user-list.component.html index c283c08..8e93473 100644 --- a/src/app/user-list/user-list.component.html +++ b/src/app/user-list/user-list.component.html @@ -1,11 +1,27 @@
- + - +
    -
  • {{ user.name }}
  • +
diff --git a/src/app/user-list/user-list.component.ts b/src/app/user-list/user-list.component.ts index 0a0ddd7..914e403 100644 --- a/src/app/user-list/user-list.component.ts +++ b/src/app/user-list/user-list.component.ts @@ -11,13 +11,21 @@ import { WebStorageService } from '../services/web-storage.service'; export class UserListComponent implements OnInit { public users: User[] | null = null; - constructor(private userListService: UserListService) {} + constructor( + private userListService: UserListService, + private webStorage: WebStorageService, + ) {} public async ngOnInit(): Promise { - this.users = await this.userListService.getAll(); + const filtered = this.webStorage.get('USERS'); + this.users = + filtered === null + ? await this.userListService.getAll() + : JSON.parse(filtered); } public async update(text: string): Promise { this.users = await this.userListService.filter(text); + this.webStorage.set('USERS', JSON.stringify(this.users)); } } diff --git a/src/styles.css b/src/styles.css index 5fecd2c..6d70f00 100644 --- a/src/styles.css +++ b/src/styles.css @@ -11,3 +11,7 @@ body { color: var(--color); font-family: var(--font-family); } + +.highlight-text { + background: yellow +} From 113511c5647f0720e5dcb71131fd7fec263fa0f3 Mon Sep 17 00:00:00 2001 From: Anoop Pemmaraju Date: Fri, 6 Feb 2026 22:28:43 +0530 Subject: [PATCH 4/5] Addig Interceptor --- .../mocks/user-list-interceptor.service.ts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/app/mocks/user-list-interceptor.service.ts diff --git a/src/app/mocks/user-list-interceptor.service.ts b/src/app/mocks/user-list-interceptor.service.ts new file mode 100644 index 0000000..e1a315d --- /dev/null +++ b/src/app/mocks/user-list-interceptor.service.ts @@ -0,0 +1,50 @@ +import { + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest, + HttpResponse, +} from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class UserListInterceptorService implements HttpInterceptor { + private readonly API_URL = '/mock/api/filter'; + private readonly STORAGE_KEY = 'MOCK_API_FILTER'; + + intercept( + request: HttpRequest, + next: HttpHandler, + ): Observable> { + if (request.url === this.API_URL && request.method === 'GET') { + return this.getFilter(); + } + + if (request.url === this.API_URL && request.method === 'PUT') { + return this.setFilter(request.body); + } + + return next.handle(request); + } + + private getFilter(): Observable> { + return new Observable((observer) => { + observer.next( + new HttpResponse({ + status: 200, + body: window.localStorage.getItem(this.STORAGE_KEY), + }), + ); + + observer.complete(); + }); + } + + private setFilter(filter: string): Observable> { + window.localStorage.setItem(this.STORAGE_KEY, filter); + return this.getFilter(); + } +} From e2774c189424af83caae433d6456aef8edb30fa8 Mon Sep 17 00:00:00 2001 From: Anoop Pemmaraju Date: Fri, 6 Feb 2026 22:30:28 +0530 Subject: [PATCH 5/5] Spec File --- .../mocks/user-list-interceptor.service.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/app/mocks/user-list-interceptor.service.spec.ts diff --git a/src/app/mocks/user-list-interceptor.service.spec.ts b/src/app/mocks/user-list-interceptor.service.spec.ts new file mode 100644 index 0000000..dccec07 --- /dev/null +++ b/src/app/mocks/user-list-interceptor.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserListInterceptorService } from './user-list-interceptor.service'; + +describe('UserListInterceptorService', () => { + let service: UserListInterceptorService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UserListInterceptorService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +});