jsverse / transloco

🚀 😍 The internationalization (i18n) library for Angular
https://jsverse.github.io/transloco/
MIT License
2.04k stars 197 forks source link

Feature(testing): add 'provideTranslocoTestingModule' or export its injection tokens. #710

Open MikeDabrowski opened 1 year ago

MikeDabrowski commented 1 year ago

Is there an existing issue for this?

Which Transloco package(s) will this feature affect?

Transloco

Is your feature request related to a problem? Please describe

In out app we use transloco + locale + messageFormat. We had to make some units that test those features so we have

export function importTranslocoTestingModule(
  config: Partial<TranslocoConfig> = {},
  langs: TranslocoTestingOptions['langs'] = {},
  preloadLangs?: TranslocoTestingOptions['preloadLangs'],
) {
  return [
    TranslocoLocaleModule.forRoot({
      langToLocaleMapping,
    }),
    TranslocoTestingModule.forRoot({
      langs: { [Language.enGB]: en, [Language.deDE]: de, ...langs },
      translocoConfig: {
        availableLangs: [Language.enGB, Language.deDE],
        defaultLang: Language.enGB,
        ...config,
      },
      preloadLangs,
    }),
    TranslocoMessageFormatModule.forRoot({
      locales: Object.keys(langToLocaleMapping),
      // @ts-ignore
      langToLocaleMapping,
    }),
  ];
}

This worked fine in v4 but upgrading it now to v5 and v6 presents some issues.

In v5+ default way of enabling transloco is using provider fns. But for test there stil is the testing module .forRoot. As you can see we used to import 3 modules, 2 of which are now moved to provider fns. The end goal here is to have a one line that can be used in TestBed's imports (or providers). So Custom testing module idea was born:

@NgModule({
  exports: [TranslocoModule],
})
export class TranslocoCustomTestingModule {
  static importTranslocoTestingModule(
    config: Partial<TranslocoConfig> = {},
    langs: TranslocoTestingOptions['langs'] = {},
    preloadLangs?: TranslocoTestingOptions['preloadLangs'],
  ): ModuleWithProviders<any> {

    const module: ModuleWithProviders<TranslocoTestingModule> =     TranslocoTestingModule.forRoot({
      langs: { [Language.enGB]: en, [Language.deDE]: de, ...langs },
      translocoConfig: {
        availableLangs: [Language.enGB, Language.deDE],
        defaultLang: Language.enGB,
        ...config,
      },
      preloadLangs,
    });

    return {
      ngModule: TranslocoCustomTestingModule,
      providers: [
        provideTransloco({
          loader: TestingLoader,
          config: {
            availableLangs: [Language.enGB, Language.deDE],
            defaultLang: Language.enGB,
            langs: { [Language.enGB]: en, [Language.deDE]: de, ...langs },
            ...config,
          },
          preloadLangs,
        }),
        {
          provide: TRANSLOCO_TEST_LANGS,
          useValue: { [Language.enGB]: en, [Language.deDE]: de, ...langs },
        },
      ],
    };
  }
}

The providers are mostly copied from source of translocoTestingModule. However the problem now is - how to get the TRANSLOCO_TEST_LANGS and TRANSLOCO_TEST_OPTIONS

Describe the solution you'd like

At this point I am not sure what would help here.

Option 1

Would be to export the TRANSLOCO_TEST_LANGS, TRANSLOCO_TEST_OPTIONS and perhaps something else.

I tried manually adding exports in built packages but it failed with

 Error: Module not found
: Error: Package path ./lib/transloco-testing.module is not exported from package ~\node_modules\@ngneat\transloco

Option 2

Would be to provide provider function for that translocoTesting module and add a way to provide additional providers.

Option 3

Do this in each place

TestBed.configureTestingModule({
      declarations: [...],
      schemas: [...],
      imports: [importTranslocoTestingModule()], // <-- as in the docs
      providers: [addTranslocoTestingProviders()] // <-- similar custom fn that takes the same params
...

Describe alternatives you've considered

We got over 300 usages of that custom function. We could go with opt3 for now but this will be huge amount of work.

Opt2 should give best results and least changes

Opt1 I'm not 100% sure if will work or require additional modifications

Additional context

No response

I would like to make a pull request for this feature

No

krechtern commented 1 year ago

We are also facing problems after the update to transloco v5/v6. We are also having a custom function which returns the TranslocoTestingModule.forRoot.

Now we are getting NullInjectorError: No provider for InjectionToken TRANSLOCO_TRANSPILER! in some tests.

Our testing module factory function looks like this at the moment:

import { TranslocoTestingModule } from '@ngneat/transloco';

export const getTranslocoTestingModule = (en = {}, de = {}) =>
  TranslocoTestingModule.forRoot({
    langs: { 'en-US': en, 'de-DE': de },
    translocoConfig: {
      defaultLang: 'en-US',
      fallbackLang: 'en-US',
      availableLangs: ['de-DE', 'en-US'],
      missingHandler: { logMissingKey: false, useFallbackTranslation: false, allowEmpty: false },
    },
    preloadLangs: true,
});
janpapenbrock commented 1 year ago

@MikeDabrowski Would maybe something like this work for you to have additional providers on the module?

export function importTranslocoTestingModule(options: TranslocoTestingOptions = {}) {
  const result = TranslocoTestingModule.forRoot({
    translocoConfig: {
      // ...
    },
    ...options
  });

  result.providers = result.providers.concat(
    provideTranslocoLocale({
      // ...
    }),
    // ...
  )

  return result;
}

@krechtern We had the same issue - turns out we had not imported getTranslocoTestingModule() in the failing tests at all. Not sure how it worked before, but checking all those tests and importing it resolved the issue for us.