这是indexloc提供的服务,不要输入任何密码
Skip to content

Partial testing by Jest #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions __tests__/deepL.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Jest tests for deepLTranslate() and deepLGetLanguages()

import {
deepLTranslate,
deepLGetLanguages,
DeepLSupportedLanguages,
DeepLLanguageType,
} from '../src/sheetsl';

const ADDON_NAME = 'SheetsL';
const DEEPL_API_BASE_URL_FREE = 'https://api-free.deepl.com/v2/';
const translateUrl = DEEPL_API_BASE_URL_FREE + 'translate';
const getLanguageUrl = DEEPL_API_BASE_URL_FREE + 'languages';
const mockApiKey = 'apiKeyString:fx';

PropertiesService.getUserProperties = jest.fn(() => ({
getProperty: jest.fn(() => mockApiKey),
})) as any;

UrlFetchApp.fetch = jest.fn((url: string, options: UrlFetchAppOptions) => ({
_options: options,
getResponseCode: jest.fn((): number => 200),
getContentText: jest.fn((text: string = url) => {
// Switch the returning value of the mock function for UrlFetchApp.fetch
// depending on the input URL.
if (text.startsWith(translateUrl)) {
// URL with the endpoint of DeepL API translation will return
// a stringified object of type DeepLTranslationResponse
return JSON.stringify({
translations: [
{
detected_source_language: 'JA',
text: text, // This mock translated text will return the string of the input URL for UrlFetchApp.fetch
},
],
});
} else if (text.startsWith(getLanguageUrl)) {
// URL with the endpoint of DeepL API to retrieve the list of supported languages
// will return a stringified list of type DeepLSupportedLanguages objects.
return JSON.stringify([
{
language: 'EN-US',
name: 'English (American)',
supports_formality: false,
},
{
language: 'JA',
name: 'Japanese',
supports_formality: false,
},
{
language: 'MOCK',
name: text, // This mock language will return the string of the input URL for UrlFetchApp.fetch
supports_formality: true,
},
]);
}
}),
})) as any;

type UrlFetchAppOptions = {
muteHttpExceptions: boolean;
};

type DeepLTranslatePattern = {
title: string;
input: DeepLTranslatePatternInput;
expectedOutput: string[];
};

type DeepLTranslatePatternInput = {
sourceText: string | string[];
sourceLocale: string | null | undefined;
targetLocale: string;
};

type DeepLGetLanguages = {
title: string;
input: DeepLLanguageType;
expectedOutput: DeepLSupportedLanguages[];
};

const deepLTranslatePatterns: DeepLTranslatePattern[] = [
{
title: 'sourceText as string',
input: {
sourceText: 'text to translate',
sourceLocale: 'JA',
targetLocale: 'EN-US',
},
expectedOutput: [
`${translateUrl}?auth_key=${mockApiKey}&target_lang=EN-US&text=${encodeURIComponent(
'text to translate'
)}&source_lang=JA`,
],
},
{
title: 'sourceText as an array of strings',
input: {
sourceText: [
'text to translate 1',
'text to translate 2',
'text to translate 3',
],
sourceLocale: 'JA',
targetLocale: 'EN-US',
},
expectedOutput: [
`${translateUrl}?auth_key=${mockApiKey}&target_lang=EN-US&text=${encodeURIComponent(
'text to translate 1'
)}&text=${encodeURIComponent(
'text to translate 2'
)}&text=${encodeURIComponent('text to translate 3')}&source_lang=JA`,
],
},
];

const deepLTranslatePatternsWithErrors: DeepLTranslatePattern[] = [
{
title: 'Empty sourceText',
input: {
sourceText: '',
sourceLocale: 'JA',
targetLocale: 'EN-US',
},
expectedOutput: ['returns an error'],
},
];

const deepLGetLanguagesPatterns: DeepLGetLanguages[] = [
{
title: 'type = source',
input: 'source',
expectedOutput: [
{
language: 'EN-US',
name: 'English (American)',
supports_formality: false,
},
{
language: 'JA',
name: 'Japanese',
supports_formality: false,
},
{
language: 'MOCK',
name: `${getLanguageUrl}?auth_key=${mockApiKey}&type=source`,
supports_formality: true,
},
],
},
{
title: 'type = target',
input: 'target',
expectedOutput: [
{
language: 'EN-US',
name: 'English (American)',
supports_formality: false,
},
{
language: 'JA',
name: 'Japanese',
supports_formality: false,
},
{
language: 'MOCK',
name: `${getLanguageUrl}?auth_key=${mockApiKey}&type=target`,
supports_formality: true,
},
],
},
];

describe.each(deepLTranslatePatterns)(
'deepLTranslate',
({ title, input, expectedOutput }) => {
test(`deepLTranslate test: ${title}`, () => {
expect(
deepLTranslate(input.sourceText, input.sourceLocale, input.targetLocale)
).toEqual(expectedOutput);
});
}
);

// Error patterns in deepLTranslate
describe.each(deepLTranslatePatternsWithErrors)(
'deepLTranslate Errors',
({ title, input }) => {
test(`deepLTranslate error test: ${title}`, () => {
expect(() => {
deepLTranslate(
input.sourceText,
input.sourceLocale,
input.targetLocale
);
}).toThrowError(new Error(`[${ADDON_NAME}] Empty input.`));
});
}
);

describe.each(deepLGetLanguagesPatterns)(
'deepLGetLanguages',
({ title, input, expectedOutput }) => {
test(`deepLGetLanguages test: ${title}`, () => {
expect(deepLGetLanguages(input)).toEqual(expectedOutput);
});
}
);
10 changes: 5 additions & 5 deletions __tests__/getDeepLApiBaseUrl.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
DEEPL_API_BASE_URL_FREE,
DEEPL_API_BASE_URL_PRO,
getDeepLApiBaseUrl,
} from '../src/sheetsl';
import { getDeepLApiBaseUrl } from '../src/sheetsl';

const DEEPL_API_VERSION = 'v2';
const DEEPL_API_BASE_URL_FREE = `https://api-free.deepl.com/${DEEPL_API_VERSION}/`;
const DEEPL_API_BASE_URL_PRO = `https://api.deepl.com/${DEEPL_API_VERSION}/`;

const patterns = [
{
Expand Down
21 changes: 21 additions & 0 deletions __tests__/getDeepLApiKey.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getDeepLApiKey } from '../src/sheetsl';

const ADDON_NAME = 'SheetsL';

PropertiesService.getUserProperties = jest.fn(() => ({
getProperty: jest.fn(() => undefined),
})) as any;

const testObj = {
title:
'Case when DeepL API Authentication Key is not saved in the user property',
errorMessage: `[${ADDON_NAME}] API Key Unavailable: Set the DeepL API Authentication Key from the Settings > Set Auth Key of the add-on menu.`,
};

describe('getDeepLApiKey Error', () => {
test(testObj.title, () => {
expect(() => {
getDeepLApiKey();
}).toThrowError(new Error(testObj.errorMessage));
});
});
70 changes: 70 additions & 0 deletions __tests__/handleDeepLErrors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { handleDeepLErrors } from '../src/sheetsl';

const ADDON_NAME = 'SheetsL';

const successPattern = {
title: 'HTTP Response Code 200',
inputResponse: {
getResponseCode: () => 200,
getContentText: () => 'Testing HTTP Response Code 200',
},
} as any;
const errorPatterns = [
{
title: 'HTTP Response Code 429',
inputResponse: {
getResponseCode: () => 429,
getContentText: () => 'Testing HTTP Response Code 429',
},
expectedErrorMessage: `[${ADDON_NAME}] Too Many Requests: Try again after some time.`,
},
{
title: 'HTTP Response Code 456',
inputResponse: {
getResponseCode: () => 456,
getContentText: () => 'Testing HTTP Response Code 456',
},
expectedErrorMessage: `[${ADDON_NAME}] Quota Exceeded: The translation limit of your account has been reached.`,
},
{
title: 'HTTP Response Code 500',
inputResponse: {
getResponseCode: () => 500,
getContentText: () => 'Testing HTTP Response Code 500',
},
expectedErrorMessage: `[${ADDON_NAME}] Temporary errors in the DeepL service. Please retry after waiting for a while.`,
},
{
title: 'HTTP Response Code 501',
inputResponse: {
getResponseCode: () => 501,
getContentText: () => 'Testing HTTP Response Code 501',
},
expectedErrorMessage: `[${ADDON_NAME}] Temporary errors in the DeepL service. Please retry after waiting for a while.`,
},
{
title: 'HTTP Response Code 300',
inputResponse: {
getResponseCode: () => 300,
getContentText: () => 'Testing HTTP Response Code 300',
},
expectedErrorMessage: `[${ADDON_NAME}] Error on Calling DeepL API: Testing HTTP Response Code 300`,
},
] as any[];

describe('handleDeepLErrors Success', () => {
test(successPattern.title, () => {
expect(handleDeepLErrors(successPattern.inputResponse)).toBeUndefined();
});
});

describe.each(errorPatterns)(
'handleDeepLErrors Errors',
({ title, inputResponse, expectedErrorMessage }) => {
test(title, () => {
expect(() => {
handleDeepLErrors(inputResponse);
}).toThrowError(new Error(expectedErrorMessage));
});
}
);
57 changes: 57 additions & 0 deletions __tests__/onInstall.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { onInstall } from '../src/sheetsl';

const ADDON_NAME = 'SheetsL';

SpreadsheetApp.getUi = jest.fn(() => ({
createAddonMenu: jest.fn(() => new MockMenu(true)),
createMenu: jest.fn((title: string) => new MockMenu(false, title)),
})) as any;

class MockMenu {
mockMenu: MockMenuObj;
isAddonMenu: boolean;
constructor(isAddonMenu: boolean, title: string = '') {
this.mockMenu = {
title: isAddonMenu ? ADDON_NAME : title,
menu: [],
isAddonMenu: isAddonMenu,
};
}
addItem(itemName: string, functionName: string): this {
this.mockMenu.menu.push({ itemName: itemName, functionName: functionName });
return this;
}
addSeparator(): this {
this.mockMenu.menu.push('---');
return this;
}
addSubMenu(menu: MockMenuObj): this {
this.mockMenu.menu.push(menu);
return this;
}
addToUi(): MockMenuObj {
return this.mockMenu;
}
}

type MockMenuObj = {
title: string;
menu: any[];
isAddonMenu: boolean;
};

describe('onOpen/onInstall', () => {
test('onOpen/onInstall test', () => {
expect(onInstall).toEqual({
title: ADDON_NAME,
menu: [
{
title: 'Settings',
menu: [{ itemName: 'Set Auth Key', functionName: 'setDeeplAuthKey' }],
isAddonMenu: false,
},
],
isAddonMenu: true,
});
});
});
Loading