2024-07-23 16:50:12 +00:00
/ * !
* @license
* Copyright Google LLC All Rights Reserved .
*
* Use of this source code is governed by an MIT - style license that can be
* found in the LICENSE file at https : //angular.dev/license
* /
2025-07-12 00:35:18 +00:00
import { ComponentFixture , TestBed } from '@angular/core/testing' ;
2024-07-23 16:50:12 +00:00
import {
CONFIRMATION_DISPLAY_TIME_MS ,
CopySourceCodeButton ,
} from './copy-source-code-button.component' ;
import {
ChangeDetectionStrategy ,
Component ,
2025-04-04 17:43:36 +00:00
provideZonelessChangeDetection ,
2024-07-23 16:50:12 +00:00
signal ,
} from '@angular/core' ;
import { By } from '@angular/platform-browser' ;
import { Clipboard } from '@angular/cdk/clipboard' ;
const SUCCESSFULLY_COPY_CLASS_NAME = 'docs-copy-source-code-button-success' ;
const FAILED_COPY_CLASS_NAME = 'docs-copy-source-code-button-failed' ;
describe ( 'CopySourceCodeButton' , ( ) = > {
let component : CodeSnippetWrapper ;
let fixture : ComponentFixture < CodeSnippetWrapper > ;
let copySpy : jasmine.Spy < ( text : string ) = > boolean > ;
beforeEach ( async ( ) = > {
TestBed . configureTestingModule ( {
imports : [ CodeSnippetWrapper ] ,
2025-04-04 17:43:36 +00:00
providers : [ provideZonelessChangeDetection ( ) ] ,
2024-07-23 16:50:12 +00:00
} ) ;
fixture = TestBed . createComponent ( CodeSnippetWrapper ) ;
component = fixture . componentInstance ;
await fixture . whenStable ( ) ;
} ) ;
beforeEach ( ( ) = > {
const clipboardService = TestBed . inject ( Clipboard ) ;
copySpy = spyOn ( clipboardService , 'copy' ) ;
} ) ;
it ( 'should call clipboard service when clicked on copy source code' , async ( ) = > {
const expectedCodeToBeCopied = 'npm install -g @angular/cli' ;
component . code . set ( expectedCodeToBeCopied ) ;
await fixture . whenStable ( ) ;
const button = fixture . debugElement . query ( By . directive ( CopySourceCodeButton ) ) . nativeElement ;
button . click ( ) ;
expect ( copySpy . calls . argsFor ( 0 ) [ 0 ] . trim ( ) ) . toBe ( expectedCodeToBeCopied ) ;
} ) ;
it ( 'should not copy lines marked as deleted when code snippet contains diff' , async ( ) = > {
const codeInHtmlFormat = `
< code >
< div class = "line remove" > < span class = "hljs-tag" > & lt ; < span class = "hljs-name" > div < / span > * < span class = "hljs-attr" > ngFor < / span > = < span class = "hljs-string" > "let product of products" < / span > & gt ; < / span > < / div >
< div class = "line add" > < span class = "hljs-tag" > & lt ; < span class = "hljs-name" > div < / span > * < span class = "hljs-attr" > ngFor < / span > = < span class = "hljs-string" > "let product of products()" < / span > & gt ; < / span > < / div >
< / code >
` ;
const expectedCodeToBeCopied = ` <div *ngFor="let product of products()"> ` ;
component . code . set ( codeInHtmlFormat ) ;
await fixture . whenStable ( ) ;
const button = fixture . debugElement . query ( By . directive ( CopySourceCodeButton ) ) . nativeElement ;
button . click ( ) ;
expect ( copySpy . calls . argsFor ( 0 ) [ 0 ] . trim ( ) ) . toBe ( expectedCodeToBeCopied ) ;
} ) ;
2025-07-12 00:35:18 +00:00
it ( ` should set ${ SUCCESSFULLY_COPY_CLASS_NAME } for ${ CONFIRMATION_DISPLAY_TIME_MS } ms when copy was executed properly ` , ( ) = > {
const clock = jasmine . clock ( ) . install ( ) ;
2024-07-23 16:50:12 +00:00
component . code . set ( 'example' ) ;
fixture . detectChanges ( ) ;
const button = fixture . debugElement . query ( By . directive ( CopySourceCodeButton ) ) . nativeElement ;
button . click ( ) ;
fixture . detectChanges ( ) ;
expect ( button ) . toHaveClass ( SUCCESSFULLY_COPY_CLASS_NAME ) ;
2025-07-12 00:35:18 +00:00
clock . tick ( CONFIRMATION_DISPLAY_TIME_MS ) ;
2024-07-23 16:50:12 +00:00
fixture . detectChanges ( ) ;
expect ( button ) . not . toHaveClass ( SUCCESSFULLY_COPY_CLASS_NAME ) ;
2025-07-12 00:35:18 +00:00
clock . uninstall ( ) ;
} ) ;
2024-07-23 16:50:12 +00:00
2025-07-12 00:35:18 +00:00
it ( ` should set ${ FAILED_COPY_CLASS_NAME } for ${ CONFIRMATION_DISPLAY_TIME_MS } ms when copy failed ` , async ( ) = > {
const clock = jasmine . clock ( ) . install ( ) ;
2024-07-23 16:50:12 +00:00
component . code . set ( 'example' ) ;
copySpy . and . throwError ( 'Fake copy error' ) ;
fixture . detectChanges ( ) ;
const button = fixture . debugElement . query ( By . directive ( CopySourceCodeButton ) ) . nativeElement ;
button . click ( ) ;
fixture . detectChanges ( ) ;
expect ( button ) . toHaveClass ( FAILED_COPY_CLASS_NAME ) ;
2025-07-12 00:35:18 +00:00
clock . tick ( CONFIRMATION_DISPLAY_TIME_MS ) ;
2024-07-23 16:50:12 +00:00
fixture . detectChanges ( ) ;
expect ( button ) . not . toHaveClass ( FAILED_COPY_CLASS_NAME ) ;
2025-07-12 00:35:18 +00:00
clock . uninstall ( ) ;
} ) ;
2024-07-23 16:50:12 +00:00
} ) ;
@Component ( {
template : `
< pre >
< code [ innerHtml ] = " code ( ) " > < / code >
< / pre >
< button docs - copy - source - code > < / button >
` ,
imports : [ CopySourceCodeButton ] ,
changeDetection : ChangeDetectionStrategy.OnPush ,
} )
class CodeSnippetWrapper {
code = signal ( '' ) ;
}