refactor(monorepo): change coverage of core to 100% (#17698)

This commit is contained in:
Yongjie Zhao 2021-12-14 16:19:55 +08:00 committed by GitHub
parent 89d0d38ed0
commit 07bbe8448b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 96 additions and 67 deletions

View File

@ -16,7 +16,7 @@ coverage:
target: auto target: auto
threshold: 0% threshold: 0%
core-packages-ts: core-packages-ts:
target: 95% target: 100%
paths: paths:
- 'superset-frontend/packages' - 'superset-frontend/packages'
- '!superset-frontend/packages/**/*.jsx' - '!superset-frontend/packages/**/*.jsx'

View File

@ -56,9 +56,10 @@ module.exports = {
coverageReporters: ['lcov', 'json-summary', 'html'], coverageReporters: ['lcov', 'json-summary', 'html'],
transform: { transform: {
'^.+\\.jsx?$': 'babel-jest', '^.+\\.jsx?$': 'babel-jest',
// ts-jest can't load plugin 'babel-plugin-typescript-to-proptypes' // ts-jest doesn't work with `--coverage`. @superset-ui/core should
'reactify\\.tsx$': 'babel-jest', // 100% coverage, so we use babel-jest in packages and plugins.
'^.+\\.tsx?$': 'ts-jest', '(plugins|packages)\\/.+\\.tsx?$': 'babel-jest',
'(((?!(plugins|packages)).)*)\\/.+\\.tsx?$': 'ts-jest',
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
snapshotSerializers: ['@emotion/jest/enzyme-serializer'], snapshotSerializers: ['@emotion/jest/enzyme-serializer'],

View File

@ -40,12 +40,7 @@ export default class CategoricalColorNamespace {
getScale(schemeId?: string) { getScale(schemeId?: string) {
const id = schemeId ?? getCategoricalSchemeRegistry().getDefaultKey() ?? ''; const id = schemeId ?? getCategoricalSchemeRegistry().getDefaultKey() ?? '';
const scheme = getCategoricalSchemeRegistry().get(id); const scheme = getCategoricalSchemeRegistry().get(id);
const newScale = new CategoricalColorScale( return new CategoricalColorScale(scheme?.colors ?? [], this.forcedItems);
scheme?.colors ?? [],
this.forcedItems,
);
return newScale;
} }
/** /**

View File

@ -33,13 +33,6 @@ import {
} from './types'; } from './types';
import { DEFAULT_FETCH_RETRY_OPTIONS, DEFAULT_BASE_URL } from './constants'; import { DEFAULT_FETCH_RETRY_OPTIONS, DEFAULT_BASE_URL } from './constants';
function redirectUnauthorized() {
// the next param will be picked by flask to redirect the user after the login
setTimeout(() => {
window.location.href = `/login?next=${window.location.href}`;
});
}
export default class SupersetClientClass { export default class SupersetClientClass {
credentials: Credentials; credentials: Credentials;
@ -159,8 +152,8 @@ export default class SupersetClientClass {
timeout: timeout ?? this.timeout, timeout: timeout ?? this.timeout,
fetchRetryOptions: fetchRetryOptions ?? this.fetchRetryOptions, fetchRetryOptions: fetchRetryOptions ?? this.fetchRetryOptions,
}).catch(res => { }).catch(res => {
if (res && res.status === 401) { if (res?.status === 401) {
redirectUnauthorized(); this.redirectUnauthorized();
} }
return Promise.reject(res); return Promise.reject(res);
}); });
@ -226,4 +219,8 @@ export default class SupersetClientClass {
endpoint[0] === '/' ? endpoint.slice(1) : endpoint endpoint[0] === '/' ? endpoint.slice(1) : endpoint
}`; }`;
} }
redirectUnauthorized() {
window.location.href = `/login?next=${window.location.href}`;
}
} }

View File

@ -16,15 +16,12 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import seedrandom from 'seedrandom'; import _seedrandom from 'seedrandom';
let random = seedrandom('superset-ui');
export function seed(seed: string) { export function seed(seed: string) {
random = seedrandom(seed); return _seedrandom(seed);
return random;
} }
export function seedRandom() { export function seedRandom() {
return random(); return _seedrandom('superset-ui')();
} }

View File

@ -17,10 +17,7 @@
* under the License. * under the License.
*/ */
import fetchMock from 'fetch-mock'; import fetchMock from 'fetch-mock';
import { import { SupersetClientClass, ClientConfig, CallApi } from '@superset-ui/core';
SupersetClientClass,
ClientConfig,
} from '@superset-ui/core/src/connection';
import { LOGIN_GLOB } from './fixtures/constants'; import { LOGIN_GLOB } from './fixtures/constants';
describe('SupersetClientClass', () => { describe('SupersetClientClass', () => {
@ -321,7 +318,7 @@ describe('SupersetClientClass', () => {
await client.init(); await client.init();
await client.get({ url: mockGetUrl }); await client.get({ url: mockGetUrl });
const fetchRequest = fetchMock.calls(mockGetUrl)[0][1]; const fetchRequest = fetchMock.calls(mockGetUrl)[0][1] as CallApi;
expect(fetchRequest.mode).toBe(clientConfig.mode); expect(fetchRequest.mode).toBe(clientConfig.mode);
expect(fetchRequest.credentials).toBe(clientConfig.credentials); expect(fetchRequest.credentials).toBe(clientConfig.credentials);
expect(fetchRequest.headers).toEqual( expect(fetchRequest.headers).toEqual(
@ -378,7 +375,7 @@ describe('SupersetClientClass', () => {
await client.init(); await client.init();
await client.get({ url: mockGetUrl, ...overrideConfig }); await client.get({ url: mockGetUrl, ...overrideConfig });
const fetchRequest = fetchMock.calls(mockGetUrl)[0][1]; const fetchRequest = fetchMock.calls(mockGetUrl)[0][1] as CallApi;
expect(fetchRequest.mode).toBe(overrideConfig.mode); expect(fetchRequest.mode).toBe(overrideConfig.mode);
expect(fetchRequest.credentials).toBe(overrideConfig.credentials); expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
expect(fetchRequest.headers).toEqual( expect(fetchRequest.headers).toEqual(
@ -423,7 +420,7 @@ describe('SupersetClientClass', () => {
await client.init(); await client.init();
await client.post({ url: mockPostUrl, ...overrideConfig }); await client.post({ url: mockPostUrl, ...overrideConfig });
const fetchRequest = fetchMock.calls(mockPostUrl)[0][1]; const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
expect(fetchRequest.mode).toBe(overrideConfig.mode); expect(fetchRequest.mode).toBe(overrideConfig.mode);
expect(fetchRequest.credentials).toBe(overrideConfig.credentials); expect(fetchRequest.credentials).toBe(overrideConfig.credentials);
@ -454,7 +451,8 @@ describe('SupersetClientClass', () => {
await client.init(); await client.init();
await client.post({ url: mockPostUrl, postPayload }); await client.post({ url: mockPostUrl, postPayload });
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData; const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
const formData = fetchRequest.body as FormData;
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1); expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
Object.entries(postPayload).forEach(([key, value]) => { Object.entries(postPayload).forEach(([key, value]) => {
@ -470,7 +468,8 @@ describe('SupersetClientClass', () => {
await client.init(); await client.init();
await client.post({ url: mockPostUrl, postPayload, stringify: false }); await client.post({ url: mockPostUrl, postPayload, stringify: false });
const formData = fetchMock.calls(mockPostUrl)[0][1].body as FormData; const fetchRequest = fetchMock.calls(mockPostUrl)[0][1] as CallApi;
const formData = fetchRequest.body as FormData;
expect(fetchMock.calls(mockPostUrl)).toHaveLength(1); expect(fetchMock.calls(mockPostUrl)).toHaveLength(1);
Object.entries(postPayload).forEach(([key, value]) => { Object.entries(postPayload).forEach(([key, value]) => {
@ -479,4 +478,36 @@ describe('SupersetClientClass', () => {
}); });
}); });
}); });
it('should redirect Unauthorized', async () => {
const mockRequestUrl = 'https://host/get/url';
const { location } = window;
// @ts-ignore
delete window.location;
// @ts-ignore
window.location = { href: mockRequestUrl };
const authSpy = jest
.spyOn(SupersetClientClass.prototype, 'ensureAuth')
.mockImplementation();
const rejectValue = { status: 401 };
fetchMock.get(mockRequestUrl, () => Promise.reject(rejectValue), {
overwriteRoutes: true,
});
const client = new SupersetClientClass({});
let error;
try {
await client.request({ url: mockRequestUrl, method: 'GET' });
} catch (err) {
error = err;
} finally {
const redirectURL = window.location.href;
expect(redirectURL).toBe(`/login?next=${mockRequestUrl}`);
expect(error.status).toBe(401);
}
authSpy.mockReset();
window.location = location;
});
}); });

View File

@ -17,16 +17,16 @@
* under the License. * under the License.
*/ */
import { logging } from '@superset-ui/core';
import Translator from '@superset-ui/core/src/translation/Translator';
import { import {
logging,
configure, configure,
t, t,
tn, tn,
addLocaleData, addLocaleData,
addTranslation, addTranslation,
addTranslations, addTranslations,
} from '@superset-ui/core/src/translation/TranslatorSingleton'; } from '@superset-ui/core';
import Translator from '../../src/translation/Translator';
import languagePackZh from './languagePacks/zh'; import languagePackZh from './languagePacks/zh';
import languagePackEn from './languagePacks/en'; import languagePackEn from './languagePacks/en';

View File

@ -19,13 +19,8 @@
/* eslint no-console: 0 */ /* eslint no-console: 0 */
import mockConsole from 'jest-mock-console'; import mockConsole from 'jest-mock-console';
import Translator from '@superset-ui/core/src/translation/Translator'; import { configure, resetTranslation, t, tn } from '@superset-ui/core';
import { import Translator from '../../src/translation/Translator';
configure,
resetTranslation,
t,
tn,
} from '@superset-ui/core/src/translation/TranslatorSingleton';
import languagePackEn from './languagePacks/en'; import languagePackEn from './languagePacks/en';
import languagePackZh from './languagePacks/zh'; import languagePackZh from './languagePacks/zh';
@ -76,4 +71,16 @@ describe('TranslatorSingleton', () => {
}); });
}); });
}); });
it('should be reset translation setting', () => {
configure();
expect(t('second')).toEqual('second');
resetTranslation();
const restoreConsole = mockConsole();
expect(t('second')).toEqual('second');
resetTranslation();
expect(t('second')).toEqual('second');
expect(console.warn).toBeCalledTimes(2);
restoreConsole();
});
}); });

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { configure, t, tn } from '@superset-ui/core/src/translation'; import { configure, t, tn } from '@superset-ui/core';
describe('index', () => { describe('index', () => {
it('exports configure()', () => { it('exports configure()', () => {

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { LanguagePack } from '@superset-ui/core/src/translation'; import { LanguagePack } from '@superset-ui/core';
const languagePack: LanguagePack = { const languagePack: LanguagePack = {
domain: 'superset', domain: 'superset',

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { LanguagePack } from '@superset-ui/core/src/translation'; import { LanguagePack } from '@superset-ui/core';
const languagePack: LanguagePack = { const languagePack: LanguagePack = {
domain: 'superset', domain: 'superset',

View File

@ -21,40 +21,41 @@
describe('logging', () => { describe('logging', () => {
beforeEach(() => { beforeEach(() => {
jest.resetModules(); jest.resetModules();
// Explicit is better than implicit jest.resetAllMocks();
console.warn = console.error = function mockedConsole(message) {
throw new Error(message);
};
}); });
it('should pipe to `console` methods', () => {
const { logging } = require('@superset-ui/core/src');
it('should pipe to `console` methods', () => {
const { logging } = require('@superset-ui/core');
jest.spyOn(logging, 'debug').mockImplementation();
jest.spyOn(logging, 'log').mockImplementation();
jest.spyOn(logging, 'info').mockImplementation();
expect(() => { expect(() => {
logging.debug(); logging.debug();
logging.log(); logging.log();
logging.info(); logging.info();
}).not.toThrow(); }).not.toThrow();
expect(() => {
logging.warn('warn');
}).toThrow('warn');
expect(() => {
logging.error('error');
}).toThrow('error');
// to support: npx jest --silent jest.spyOn(logging, 'warn').mockImplementation(() => {
const spy = jest.spyOn(logging, 'trace'); throw new Error('warn');
spy.mockImplementation(() => { });
expect(() => logging.warn()).toThrow('warn');
jest.spyOn(logging, 'error').mockImplementation(() => {
throw new Error('error');
});
expect(() => logging.error()).toThrow('error');
jest.spyOn(logging, 'trace').mockImplementation(() => {
throw new Error('Trace:'); throw new Error('Trace:');
}); });
expect(() => { expect(() => logging.trace()).toThrow('Trace:');
logging.trace();
}).toThrow('Trace:');
spy.mockRestore();
}); });
it('should use noop functions when console unavailable', () => { it('should use noop functions when console unavailable', () => {
const { console } = window; const { console } = window;
Object.assign(window, { console: undefined }); Object.assign(window, { console: undefined });
const { logging } = require('@superset-ui/core/src'); const { logging } = require('@superset-ui/core');
afterAll(() => { afterAll(() => {
Object.assign(window, { console }); Object.assign(window, { console });