Repository: boardgameio/boardgame.io (ts)

Issue:
Updating logging functions for spying after framework is loaded
Tangentially related to #592, I propose we update how we call the logging methods `console.error` and `console.log` so they can be properly spied upon during tests after the framework has loaded. 

For context i'm using Angular CLI to generate an app. I then pull in boardgame.io and start writing tests. Angular CLI is using Typescript  4.1.2 and jasmine 3.6.0. When writing a test it looks something like this:

```
import { Client } from 'boardgame.io/client';
...


describe('MyGame', () => {
  beforeEach(() => {
    spyOn(console, 'error');
    game = Client({ game: MyGame });
    ...
  });

  it('should not do a thing', () => {
    game.moves.someMove();
    expect(console.error).toHaveBeenCalledWith('ERROR:', `disallowed move: someMove`);
  });
});
```

At the moment the expectation will fail because the boardgame code is being loaded before the spy can be created. Per #592 there is no other good way to determine if an invalid move has been made (without in depth state checking) so spying on the error logs is the only other approach. Although I suspect spying on the logs can provide other test benefits besides just invalid move testing. Since the boardgame code is binding directly to the `console.error` function reference there is no way to spy on it after the fact. 

IF spying on logging functions after the codebase has been loaded is a valid use-case then I can think of two possible approaches:
1. Allowing the client to provide both a log and error function which defaults to `console.XYZ` method, respectively.
2. Update the logger implementation to reference a function that calls the `console.XYZ` methods.

Pull request description:
Updating logging methods to be spied upon after framework is loaded. #917
Closes #917 

Allows logging to be spied after the framework has been loaded. Eases testing of invalid moves.


Patch:
diff --git a/src/core/logger.ts b/src/core/logger.ts
index 4717e92..109411e 100644
--- a/src/core/logger.ts
+++ b/src/core/logger.ts
@@ -7,8 +7,8 @@
  */
 
 const production = process.env.NODE_ENV === 'production';
-const logfn = production ? () => {} : console.log;
-const errorfn = console.error;
+const logfn = production ? () => {} : (...msg) => console.log(...msg);
+const errorfn = (...msg) => console.error(...msg);
 
 export function info(msg: string) {
   logfn(`INFO: ${msg}`);


Test patch:
diff --git a/src/core/logger.test.js b/src/core/logger.test.js
index f9431aa..f754ce1 100644
--- a/src/core/logger.test.js
+++ b/src/core/logger.test.js
@@ -51,6 +51,9 @@ describe('logging', () => {
       jest.resetModules();
       logging = require('./logger');
     });
+    afterAll(() => {
+      process.env.NODE_ENV = oldNodeEnv;
+    });
 
     test('info stripped', () => {
       logging.info('msg2');
@@ -62,4 +65,26 @@ describe('logging', () => {
       expect(console.error).toHaveBeenCalled();
     });
   });
+
+  describe('spying after load', () => {
+    let logging;
+
+    beforeAll(() => {
+      jest.resetModules();
+      console.log = oldConsoleLog;
+      console.error = oldConsoleError;
+      logging = require('./logger');
+      console.log = jest.fn();
+      console.error = jest.fn();
+    });
+
+    test('should allow console log to be spied', () => {
+      logging.info('test');
+      expect(console.log).toHaveBeenCalledWith('INFO: test');
+    });
+    test('should allow console error to be spied', () => {
+      logging.error('test');
+      expect(console.error).toHaveBeenCalledWith('ERROR:', 'test');
+    });
+  });
 });