thymikee / jest-preset-angular

Jest configuration preset for Angular projects.
https://thymikee.github.io/jest-preset-angular/
MIT License
883 stars 305 forks source link

NativeScript Support #994

Open wSedlacek opened 3 years ago

wSedlacek commented 3 years ago

🚀 Feature Proposal

Support NativeScriptTestingModule.

Motivation

NativeScript is maturing a lot, recently doing a complete overhaul of their Angular support.

Example

From what I can tell from their documentation the only major blocker here is the hard coded browser testing module.

If that could be injected to be arbitrary then every thing else can be handled in the project configuration.

import '@nativescript/core/globals';
import '@nativescript/angular/polyfills';
import '@nativescript/zone-js/dist/pre-zone-polyfills';
import 'zone.js';
import '@nativescript/zone-js';
import 'zone.js/testing';
import { TestBed } from '@angular/core/testing';
import { NativeScriptTestingModule } from '@nativescript/angular/testing';
import { platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

TestBed.initTestEnvironment(NativeScriptTestingModule, platformBrowserDynamicTesting());
ahnpnl commented 3 years ago

Look quite similar to the current setup file we currently have https://github.com/thymikee/jest-preset-angular/blob/master/src/config/setup-jest.ts , small differences are the importing of nativescript files.

wSedlacek commented 3 years ago

When playing around with it I found this error.

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    import { Observable } from '../data/observable';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

    > 1 | import '@nativescript/core/globals';
        | ^
      2 | import '@nativescript/angular/polyfills';
      3 | import '@nativescript/zone-js/dist/pre-zone-polyfills';
      4 | import 'jest-preset-angular';

Seems like we need commonjs nativescript modules to work under jest 🤔 Maybe a transformer can solve this without needing nativescript to change their module format.

ahnpnl commented 3 years ago

that error is because Jest detects that the import { Observable } from '../data/observable'; exists in import '@nativescript/core/globals'; is not CommonJS module.

You can use transformIgnorePatterns to tell Jest to explicitly transform the files you need. It looks to me like '@nativescript/core/globals' is shipped as ESM module.

Another way is using Jest in ESM mode.

wSedlacek commented 3 years ago

Great! We are making progress. Got the esm errors out of the way.

Now to figure out the resolutions for .ios.js and .android.js files.

    Cannot find module '../timer' from 'node_modules/@nativescript/core/globals/index.js'

Screen Shot 2021-09-08 at 00 20 53

ahnpnl commented 3 years ago

that one requires moduleNameMapper to be configured :)

wSedlacek commented 3 years ago

Looks like this did the trick for that one.

  haste: {
    defaultPlatform: 'ios',
    platforms: ['ios', 'android', 'native'],
  },

Although we may have found the limit of this as we are getting into missing globals.

  â—Ź Test suite failed to run

    ReferenceError: NSObject is not defined

      at Object.<anonymous> (node_modules/@nativescript/core/timer/index.ios.js:55:3)
      at Object.loader (packages/core/globals/index.ts:304:40)
      at loadModule (packages/core/globals/index.ts:244:31)
      at Window.get [as setTimeout] (packages/core/globals/index.ts:21:21)
      at getNodeSystem (../../node_modules/typescript/lib/typescript.js:7432:29)
      at ../../node_modules/typescript/lib/typescript.js:7980:19
      at ../../node_modules/typescript/lib/typescript.js:7987:7
      at Object.<anonymous> (../../node_modules/typescript/lib/typescript.js:7997:3)
      at ../../../../packages/compiler-cli/src/ngtsc/reflection/src/host.ts:9:1
      at ../../node_modules/@angular/compiler-cli/src/ngtsc/reflection/src/host.js:10:17
      at Object.<anonymous> (../../node_modules/@angular/compiler-cli/src/ngtsc/reflection/src/host.js:16:3)
      at ../../../../packages/compiler-cli/src/ngtsc/reflection/index.ts:9:1
      at ../../node_modules/@angular/compiler-cli/src/ngtsc/reflection/index.js:10:17
      at Object.<anonymous> (../../node_modules/@angular/compiler-cli/src/ngtsc/reflection/index.js:16:3)
      at Object.<anonymous> (src/test-setup.ts:4:1)
      at TestScheduler.scheduleTests (../../node_modules/@jest/core/build/TestScheduler.js:333:13)
      at runJest (../../node_modules/@jest/core/build/runJest.js:387:19)
      at _run10000 (../../node_modules/@jest/core/build/cli/index.js:408:7)
      at Object.runCLI (../../node_modules/@jest/core/build/cli/index.js:261:3)
ahnpnl commented 3 years ago

NSObject must be defined in some of the scripts which you need to let that script run first to setup global objects before node_modules/@nativescript/core/timer/index.ios.js is loaded

wSedlacek commented 3 years ago

I think that is intended to be bound to a native implementation via the NativeScript runtime, so not sure we can solve that in a node environment without polly filling it or something.

In any case, it would be nice to configure that testing module maybe an interface like this.

import { configurePreset } from 'jest-preset-angular';
import { NativeScriptTestingModule } from '@nativescript/angular/testing';

import '@nativescript/core/globals';
import '@nativescript/angular/polyfills';
import '@nativescript/zone-js/dist/pre-zone-polyfills';
configurePreset(NativeScriptTestingModule);
import '@nativescript/zone-js';

Not sure this would give the exact execution ordered needed given the import side effects. Iirc there is some hoisting going on there some where.

ahnpnl commented 3 years ago

Regarding to global Node objects, you might need to do

import { NSObject } from <somewhere>

global['NSObject']= NSObject;

something like that

I think that is intended to be bound to a native implementation via the NativeScript runtime, so not sure we can solve that in a node environment without polly filling it or something.

In any case, it would be nice to configure that testing module maybe an interface like this.

import { configurePreset } from 'jest-preset-angular';
import { NativeScriptTestingModule } from '@nativescript/angular/testing';

import '@nativescript/core/globals';
import '@nativescript/angular/polyfills';
import '@nativescript/zone-js/dist/pre-zone-polyfills';
configurePreset(NativeScriptTestingModule);
import '@nativescript/zone-js';

Not sure this would give the exact execution ordered needed given the import side effects. Iirc there is some hoisting going on there some where.

I think for NativeScript, the current setup won't be reusable. It looks to me like NativeScript environment would require different set of imported files.

I was thinking about having 2 separate setup files, one is the existing one, one is for NativeScript. So you would do in your local setup jest file

import 'jest-preset-angular/setup-jest-nativescript'

for example.

Also there would be separate presets for NativeScript which can be created in https://github.com/thymikee/jest-preset-angular/tree/master/presets

wSedlacek commented 3 years ago

Having a separate entry point would be fine great if you want to go the whole 9 yards in support.

ahnpnl commented 3 years ago

do you have an example repo of Ns + Angular with some example tests?

wSedlacek commented 3 years ago

I am still in preliminary investigation of the NativeScript platform myself, however their demo app does have some test built into it.

https://github.com/NativeScript/angular/tree/main/apps/nativescript-demo-ng

ahnpnl commented 3 years ago

I looked a bit today about the NSObject. It seems like something in C or C++ which isn’t exposed by default as a global script setup.

My guess is during the start up of the dev server, ns command somehow takes care of “transforming” C/C++ script into js which can create that object?

The main problem now when using Jest with jsdom is the potential global objects from C/C++ not available to import.

I also checked a bit about Detox but it seems to work in a different way since it requires to build the app first before running tests.

The positive point is the approach of Detox is also possibly the approach which we want to change also for the existing Angular Jest integration.

Another thing I haven’t tried is: whether or not we can reuse what we are importing for existing Jest Angular integration instead of things from polyfill like you mentioned in the description.

Fafnur commented 2 years ago

@ahnpnl you have a working example with NS and jest?

ahnpnl commented 2 years ago

Sorry we don’t have at the moment. I did a few attempts to set it up but wasn’t quite successful.