capacitor-community / privacy-screen

⚡️ Capacitor plugin that protects your app from displaying a screenshot in Recents screen/App Switcher.
MIT License
88 stars 23 forks source link

bug: Plugin fine on devices but Jasmine crashes before launching unit tests #38

Closed scottlikestech closed 2 years ago

scottlikestech commented 2 years ago

Plugin version: 2.1.0

Platform(s): Jasmine (web)

Current behavior: With regards to the app, the plugin works fine. But doing an ng test causes Jasmine to crash:

./node_modules/@capacitor-community/privacy-screen/dist/esm/index.js:2:22-36 - Error: export 'registerPlugin' (imported as 'registerPlugin') was not found in '@capacitor/core' (possible exports: Capacitor)

./node_modules/@capacitor-community/privacy-screen/dist/esm/web.js:3:38-47 - Error: export 'WebPlugin' (imported as 'WebPlugin') was not found in '@capacitor/core' (possible exports: Capacitor)

Expected behavior: Jasmine should run all its tests

Steps to reproduce: Run ng test

Related code: Just either the presence of PrivacyScreen.enable() or PrivacyScreen.disable() in the ts file, or the presence of a spyOn(PrivacyScreen, 'enable') or spyOn(PrivacyScreen, 'disable') will cause the Jasmine crash.

insert short code snippets here

The service file:

import { Injectable } from '@angular/core'; import { PrivacyScreen } from '@capacitor-community/privacy-screen';

@Injectable({ providedIn: 'root' }) export class ScreenCaptureService {

constructor(
) {
}

public async toggleProtection(protect: boolean): Promise<void> {
    if (protect) {
        await PrivacyScreen.enable();
    }
    else {
        await PrivacyScreen.disable();
    }
}

}

The test file: import { TestBed } from '@angular/core/testing'; import { PrivacyScreen } from '@capacitor-community/privacy-screen'; import { ScreenCaptureService } from './screen-capture.service';

describe('ScreenCaptureService', () => { let service: ScreenCaptureService;

beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [
        ]
    });
    service = TestBed.inject(ScreenCaptureService);
});

fit('should be created', () => {
    expect(service).toBeTruthy();
});

fit('should call PrivacyScreen.enable', async () => {
    spyOn(PrivacyScreen, 'enable');

    await service.toggleProtection(true); 

    expect(PrivacyScreen.enable).toHaveBeenCalled();
});

fit('should call PrivacyScreen.disable', async () => {
    spyOn(PrivacyScreen, 'disable');

    await service.toggleProtection(false);

    expect(PrivacyScreen.disable).toHaveBeenCalled();
});

});

Other information: My package.json dependencies and dev dependencies: "dependencies": { "@angular/animations": "~12.2.8", "@angular/common": "~12.2.8", "@angular/core": "~12.2.8", "@angular/forms": "~12.2.8", "@angular/platform-browser": "~12.2.8", "@angular/platform-browser-dynamic": "~12.2.8", "@angular/router": "~12.2.8", "@capacitor-community/electron": "4.0.3", "@capacitor-community/privacy-screen": "^2.1.0", "@capacitor/android": "^3.2.4", "@capacitor/app": "^1.0.3", "@capacitor/camera": "^1.1.0", "@capacitor/core": "^3.2.4", "@capacitor/device": "^1.0.3", "@capacitor/filesystem": "^1.0.3", "@capacitor/geolocation": "^1.1.0", "@capacitor/haptics": "^1.1.0", "@capacitor/ios": "^3.2.4", "@capacitor/keyboard": "^1.1.0", "@capacitor/local-notifications": "^1.0.6", "@capacitor/network": "^1.0.3", "@capacitor/push-notifications": "^1.0.4", "@capacitor/splash-screen": "^1.1.3", "@capacitor/status-bar": "^1.0.3", "@ionic-native/core": "^5.36.0", "@ionic-native/file-opener": "^5.36.0", "@ionic-native/image-picker": "^5.36.0", "@ionic-native/in-app-browser": "^5.36.0", "@ionic-native/speech-recognition": "^5.36.0", "@ionic-native/splash-screen": "^5.36.0", "@ionic-native/sqlite": "^5.36.0", "@ionic-native/status-bar": "^5.36.0", "@ionic-native/text-to-speech": "^5.36.0", "@ionic/angular": "^5.8.1", "@ionic/pwa-elements": "^3.0.2", "@ng-idle/core": "^11.1.0", "@ngrx/effects": "^12.4.0", "@ngrx/store": "^12.4.0", "@ngx-translate/core": "13.0.0", "@ngx-translate/http-loader": "6.0.0", "@robingenz/capacitor-background-task": "^1.0.0", "cordova-plugin-androidx-adapter": "^1.1.3", "cordova-plugin-device": "^2.0.3", "cordova-plugin-file-opener2": "^3.0.5", "cordova-plugin-inappbrowser": "^5.0.0", "cordova-plugin-speechrecognition": "^1.2.0", "cordova-plugin-telerik-imagepicker": "^2.3.6", "cordova-plugin-tts": "^0.2.3", "cordova-sqlite-storage": "^6.0.0", "core-js": "^3.18.1", "date-fns": "^2.24.0", "hammerjs": "^2.0.8", "json-formatter-js": "^2.3.4", "leaflet": "1.7.1", "lodash-es": "^4.17.21", "mobile-drag-drop": "^2.3.0-rc.2", "ngx-build-plus": "^10.1.1", "ngx-drag-drop": "^2.0.0", "pako": "^2.0.4", "rxjs": "^7.3.1", "signature_pad": "^3.0.0-beta.4", "swiper": "^7.3.0", "tslib": "^2.3.1", "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~12.2.8", "@angular/cli": "^12.2.8", "@angular/compiler": "~12.2.8", "@angular/compiler-cli": "~12.2.8", "@angular/language-service": "~12.2.8", "@capacitor/cli": "^3.2.4", "@ionic/angular-toolkit": "^4.0.0", "@ionic/cli": "^6.18.1", "@ngrx/schematics": "^12.4.0", "@ngrx/store-devtools": "^12.4.0", "@types/jasmine": "~3.9.1", "@types/jasminewd2": "~2.0.10", "@types/leaflet": "^1.7.5", "@types/lodash-es": "^4.17.4", "@types/node": "^14.14.41", "@types/pako": "1.0.0", "@types/signature_pad": "^2.3.1", "@types/sqlite3": "^3.1.6", "@typescript-eslint/eslint-plugin": "~4.9.0", "@typescript-eslint/parser": "~4.9.0", "chalk": "^4.1.0", "clipboardy": "^2.3.0", "codelyzer": "^6.0.1", "cordova-res": "^0.15.3", "eslint": "^7.14.0", "eslint-plugin-import": "^2.22.1", "jasmine-core": "~3.9.0", "jasmine-spec-reporter": "~7.0.0", "jetifier": "^1.6.6", "karma": "~6.3.4", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.1", "karma-jasmine-html-reporter": "^1.7.0", "karma-parallel": "^0.3.1", "karma-verbose-reporter": "^0.0.6", "ng-mocks": "^12.5.0", "npm-run-all": "^4.1.5", "protractor": "~7.0.0", "sqlite-parser": "^1.0.1", "ts-node": "~9.0.0", "typescript": "4.3.5", "xml-formatter": "^2.5.1", "xml2json": "^0.12.0" }

Running your sample app on the same machine with this plugin with its unit tests is fine, so I suspect its a reference or project setting thing or the like.

Capacitor doctor:


insert the output from `npx cap doctor` here
Latest Dependencies:

  @capacitor/cli: 3.3.3
  @capacitor/core: 3.3.3
  @capacitor/android: 3.3.3
  @capacitor/ios: 3.3.3

Installed Dependencies:

  @capacitor/cli: 3.2.4
  @capacitor/core: 3.2.4
  @capacitor/android: 3.2.4
  @capacitor/ios: 3.2.4

[success] Android looking great! 👌
[error] Xcode is not installed
scottlikestech commented 2 years ago

Updating the references to satisfy the output of the capacitor doctor has had no effect.

robingenz commented 2 years ago

Hi @scottlikestech, thank you for your interest in the plugin. Please follow this guide to mock Capacitor plugins: https://capacitorjs.com/docs/guides/mocking-plugins Let me know if it still doesn't work.

scottlikestech commented 2 years ago

Hi @robingenz I have it working now. It requires a mock of the core and not merely the plugin. I noticed in your sample app you have the Jasmine tests for this plugin marked as xit(), otherwise it complains about the web platform not supporting it, but I am able to spy and test against the enable() and disable() methods now with the fix I have.

robingenz commented 2 years ago

I am glad that it works now. In that case I will close this issue.

scottlikestech commented 2 years ago

Hi @robingenz If you are interested, this is the mock file for the @capacitor/core.ts to get it to work in unit tests.

/ eslint-disable @typescript-eslint/no-unused-vars /

export const Capacitor = { isPluginAvailable(name: string): boolean { return true; }, convertFileSrc: (filePath: string): string => '' };

export const registerPlugin = (pluginName: string, implementations?: Readonly): any => { switch (pluginName) { case 'PrivacyScreen': { return { enable: () => {}, disable: () => {} }; } } return {}; };

export const WebPlugin = {};

export declare type PluginImplementations = { [platform: string]: (() => Promise) | any; };