How to unit test clipboard copy in angular?
Asked Answered
M

4

6

How to spy on clipboard.copy method? For

const clipboard = TestBed.inject(Clipboard);
spyOn(clipboard, 'copy').and.returnValue(true);

I get warning that

Argument of type '"copy"' is not assignable to parameter of type 'keyof Clipboard'.

enter image description here

I've also tried to add this to imports and declarations: I've also tried to add this to imports and declarations

This is CopyToClipboardHost

class CopyToClipboardHost {
  public content = '';
  public attempts = 1;
  public copied = jasmine.createSpy('copied spy');
}
Mccabe answered 13/10, 2021 at 12:32 Comment(3)
Can you provide code for Clipboard?Calli
In the constructor I have private clipboard: Clipboard It's a standard Angular Clipboard class material.angular.io/cdk/clipboard/overviewRimola
I've tried to use the information provided here github.com/angular/components/blob/master/src/cdk/clipboard/… But doesn't workRimola
C
5

I don't know why it didn't work within your case but I manage to create simple test case and it's working properly:

import {Component} from '@angular/core';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {Clipboard} from '@angular/cdk/clipboard';
import createSpyObj = jasmine.createSpyObj;

@Component({
  selector: 'app-root',
  template: ''
})
export class SampleComponent {
  constructor(private clipboard: Clipboard) {
  }

  copySomething(): void {
    this.clipboard.copy('test');
  }
}

describe('SampleComponent', () => {
  let fixture: ComponentFixture<SampleComponent>;
  let component: SampleComponent;
  const clipboardSpy = createSpyObj<Clipboard>('Clipboard', ['copy']);

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [SampleComponent],
      providers: [{provide: Clipboard, useValue: clipboardSpy}]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(SampleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should call clipboard copy', () => {
    component.copySomething();

    expect(clipboardSpy.copy).toHaveBeenCalledWith('test');
  });
});

One thing to note - do not import external modules to TestingModule as you want to test only your component rather do mock/spy upon required dependencies.

Calli answered 15/10, 2021 at 8:30 Comment(5)
I've added the imports and for this: const clipboardSpy = createSpyObj<Clipboard>('Clipboard', ['copy']); I get this is VsCode: Type '"copy"' is not assignable to type 'keyof Clipboard'.ts(2322). And this in WebStorm: TS2322: Type '"copy"' is not assignable to type '"readText" | "writeText" | "addEventListener" | "dispatchEvent" | "removeEventListener"'. Which IDE do you use? Maybe something is wrong with my IDEsRimola
@Mccabe I'm using Webstorm, are you sure that Clipboard you did import is right one and not something else?Calli
It's from import { Clipboard } from '@angular/cdk/clipboard';Rimola
@Mccabe hard to say, it should work but instead your IDE think is some other Clipboard.Calli
Worked perfectly, thank you!Misprint
F
4

I ran into this issue myself today, and it may have just been a different/newer implementation of the Clipboard class. In my case navigator.clipboard.writeText(text) was being used, so I had to replace

spyOn(clipboard, 'copy').and.returnValue(true);

with

spyOn(clipboard, 'writeText').and.returnValue(true);

Hope this helps someone if not you.

Fimbria answered 4/7, 2023 at 11:43 Comment(0)
A
3

You can import ClipboardModule and provide Clipboard in to TestBed. Then create a spy on copy method.

import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard';

beforeEach(async () => {
    await TestBed.configureTestingModule({
        declarations: [ExampleComponent],
        imports: [ NoopAnimationsModule, ClipboardModule],
        providers: [ Clipboard ]
    }).compileComponents();
});

it('should copy to clipboard', () => {
    const clipSpy = spyOn(component.clipboard, 'copy').and.returnValue(true);
    component.copy();
    expect(clipSpy).toHaveBeenCalled();
});
Alterable answered 15/11, 2022 at 10:23 Comment(0)
T
0

I ran into this same problem. I use jest to test my Angular apps, not jasmine, so I needed a slightly different solution, but by and large it mirrors Buczkowski's solution above:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard';
import { FixedWidthTextBoxComponent } from './fixed-width-textbox.component';

describe('FixedWidthTextBoxComponent', () => {
  let component: FixedWidthTextBoxComponent;
  let fixture: ComponentFixture<FixedWidthTextBoxComponent>;

  // I can get along without a spy here. I don't know what I would have
  // done had I needed a spy, rather than a mock
  const mockWriteText = jest.fn();

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [FixedWidthTextBoxComponent, ClipboardModule],
      // I needed to pass an object here, rather than the mock function, 
      // because I'm using the "CdkCopyToClipboard" directive from Angular
      // CDK to handle the copying to the clipboard
      providers: [{ provide: Clipboard, useValue: { copy: mockWriteText } }],
    }).compileComponents();
    fixture = TestBed.createComponent(FixedWidthTextBoxComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('Copies data to the clipboard when the button is clicked', async () => {
    component.contents = 'blah blah blah';
    component.filename = 'testfile.txt';
    component.label = 'Test Label';
    fixture.detectChanges();

    const buttons: HTMLButtonElement[] =
      fixture.nativeElement.querySelectorAll('div button');
    let copyButton!: HTMLButtonElement;

    buttons.forEach((button) => {
      if (button.textContent === 'Copy') {
        copyButton = button;
      }
    });
    copyButton.click();
    expect(mockWriteText).toHaveBeenCalledWith(component.contents);
  });
});


Twine answered 4/12, 2023 at 23:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.