toonvanstrijp / nestjs-i18n

The i18n module for nestjs.
https://nestjs-i18n.com
Other
643 stars 108 forks source link

I18n context not found in e2e test for microservice #455

Open Brakebein opened 1 year ago

Brakebein commented 1 year ago

I'm getting once again the error that the I18n context couldn't be found (#435).

ERROR [I18nService] I18n context not found! Is this function triggered by a processor or cronjob? Please use the I18nService

The microservice works nicely. But I now want to implement some e2e tests, and when executing the test, the error is thrown and the test fails.

The app successfully initializes, but as soon as I call client.send(), the error is thrown.

@Controller()
export class AppController {
  @MessagePattern({ type: 'pdf', context: 'project' })
  async generatePdf(@Payload() data, @I18n() i18n: I18nContext) {
    i18n.translate('key');
    // some implementation
    return 'some-string';
  }
}
describe('AppController (e2e)', () => {
  let app: INestApplication;
  let client: ClientProxy;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [
        AppModule,
        ClientsModule.register([
        { name: 'ExportService', transport: Transport.TCP }
      ])],
    })
      .overrideProvider(I18N_LOADER_OPTIONS)
      .useValue({ path: join(__dirname, '../../../../libs/i18n/src/lib/translations/')})
      .compile();

    app = moduleFixture.createNestApplication();
    app.connectMicroservice({
      transport: Transport.TCP
    });
    await app.startAllMicroservices();
    await app.init();

    client = app.get<ClientProxy>('ExportService');
    await client.connect();
  });

  it('should return base64-encoded file', async () => {
    const response = await lastValueFrom(client.send({ type: 'pdf', context: 'project' }, { someData: true, lang: 'de' }))

    expect(typeof response === 'string').toBeTruthy();
  });

  afterAll(async () => {
    await app.close();
    client.close()
  });
});

I'm using version nestjs-i18n@10.2.6.

toonvanstrijp commented 1 year ago

@Brakebein so it only gives this error while running tests and not in normal conditions?

Brakebein commented 1 year ago

Yes, exactly.

Brakebein commented 1 year ago

As a workaround, I managed to mock the creation of the I18nContext. As long as I don't test that the actual translations are correct according to the language key, this works so far and the test doesn't fail.

const i18nService = app.get(I18nService);
jest.spyOn(I18nContext, 'current').mockImplementation(() => new I18nContext('de', i18nService));
matzeso commented 1 year ago

I am wrapping the i18n module in my own module that has it's own t function that is already typed (so I don't need to import types in multiple places. I am running into the same issue: when I use my module in a controller it translates nicely, but if I create a testing module from my AppModule and try to use the app .get to inject my service, the context's current function returns undefined.

humblecoder commented 1 year ago

@Brakebein @matzeso apologies if I've missed something in the documentation, but, can you post an example (top-to-bottom) of using the i18nService in a context other than http request or test? For example, say I have a @Cron task that simply outputs notifications on a regular schedule in x-language?

How does one successfully instantiate the i18n service for common use in basically any "service.ts" context? I imagine something like: I18nContext.current().i18n.t... but that somehow also seems wrong. 🤔

Brakebein commented 1 year ago

Sorry, I don't have any use case for @Cron together with i18n. But shouldn't it be possible to just instantiate the I18nContext (as it works in my mocked implementation above)?

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  constructor(@Inject(I18nService) private readonly i18nService: I18nService) {}

  @Cron('45 * * * * *')
  async handleCron() {
    const i18n = new I18nContext('en', this.i18nService);
    this.logger.debug(await i18n.t('key'));
  }
}
rubiin commented 1 year ago

@Brakebein if you are manually specifying language, you dont need the context. You can do

return this.i18n.t('test.HELLO',{ lang:   'en' });