Skip to content
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

Update jest tests #149

Merged
merged 6 commits into from
Feb 7, 2024
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
6 changes: 6 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ parserOptions:
project: true
ecmaVersion: 2018
sourceType: module
overrides:
- files: ['tests/**/*.ts']
plugins: ['jest']
rules:
'@typescript-eslint/unbound-method': 'off'
'jest/unbound-method': 'error'

###########
# Plugins #
Expand Down
169 changes: 169 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"eslint": "^8.47.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-googleappsscript": "^1.0.5",
"eslint-plugin-jest": "^27.6.3",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.0.3",
Expand Down
60 changes: 29 additions & 31 deletions src/sheetsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,29 @@
* GET request on /v2/languages returns an array of this object.
* @see https://www.deepl.com/docs-api/general/get-languages/
*/
export type DeepLSupportedLanguages = {
export interface DeepLSupportedLanguages {
language: string;
name: string;
supports_formality: boolean;
};
}

/**
* The response from the DeepL API for POST /v2/translate.
* @see https://www.deepl.com/docs-api/translate-text/
*/
type DeepLTranslationResponse = {
interface DeepLTranslationResponse {
translations: DeepLTranslationObj[];
};
}

/**
* The individual translated text object in the translated response
* from DeepL API.
* @see https://www.deepl.com/docs-api/translate-text/
*/
type DeepLTranslationObj = {
interface DeepLTranslationObj {
detected_source_language: string;
text: string;
};
}

/**
* The type of language that should be returned in the GET request
Expand All @@ -64,9 +64,7 @@
* The type of the object containing key-values pairs to set in the properties of the Google Apps Script.
* @see https://developers.google.com/apps-script/reference/properties/properties#setpropertiesproperties
*/
type PropertiesObj = {
[key: string]: string;
};
type PropertiesObj = Record<string, string>;

/**
* Create add-on menu on opening spreadsheet file.
Expand All @@ -77,8 +75,8 @@
.addSubMenu(
ui
.createMenu('Settings')
.addItem('Set Auth Key', 'setDeeplAuthKey')
.addItem('Delete Auth Key', 'deleteDeeplAuthKey')
.addItem('Set DeepL API Key', 'setDeeplApiKey')
.addItem('Delete DeepL API Key', 'deleteDeeplApiKey')
.addSeparator()
.addItem('Set Language', 'setLanguage'),
)
Expand All @@ -97,14 +95,14 @@
/**
* Store DeepL API authentication key in user property.
*/
export function setDeeplAuthKey(): void {
export function setDeeplApiKey(): void {
const ui = SpreadsheetApp.getUi();
try {
const promptResponse = ui.prompt(
'Enter your DeepL API Authentication Key',
ui.ButtonSet.OK_CANCEL,
);
const apiKey = verifyAuthKeyPrompt(promptResponse, ui).getResponseText();
const apiKey = verifyApiKeyPrompt(promptResponse, ui).getResponseText();
PropertiesService.getUserProperties().setProperty(
UP_KEY_DEEPL_API_KEY,
apiKey,
Expand All @@ -119,14 +117,14 @@
}

/**
* Verify the prompt response in setDeeplAuthKey and return an error
* Verify the prompt response in setDeeplApiKey and return an error
* if the prompt is canceled or if an invalid DeepL API Authentication Key
* was entered.
* @param promptResponse Response object for the user prompt in setDeeplAuthKey
* @param promptResponse Response object for the user prompt in setDeeplApiKey
* to enter the user's DeepL API Authentication Key.
* @returns The entered prompt response object.
*/
export function verifyAuthKeyPrompt(
export function verifyApiKeyPrompt(
promptResponse: GoogleAppsScript.Base.PromptResponse,
ui: GoogleAppsScript.Base.Ui,
): GoogleAppsScript.Base.PromptResponse {
Expand All @@ -147,7 +145,7 @@
/**
* Delete the stored DeepL API authentication key in user property.
*/
export function deleteDeeplAuthKey(): void {
export function deleteDeeplApiKey(): void {
const ui = SpreadsheetApp.getUi();
try {
PropertiesService.getUserProperties().deleteProperty(UP_KEY_DEEPL_API_KEY);
Expand All @@ -163,85 +161,85 @@
/**
* Set source and target languages for translation.
*/
export function setLanguage(): void {
const ui = SpreadsheetApp.getUi();
try {
const up = PropertiesService.getUserProperties();
const userProperties = up.getProperties();
// Proceed?
if (userProperties[UP_KEY_TARGET_LOCALE]) {
// Ask user whether to proceed if a target language is already set
const alertProceed = ui.alert(
`There is an existing language setting:\n - Source Language: ${userProperties[UP_KEY_SOURCE_LOCALE]}\n - Target Language: ${userProperties[UP_KEY_TARGET_LOCALE]}\n\nDo you want to update these values?`,
ui.ButtonSet.YES_NO,
);
if (alertProceed !== ui.Button.YES) {
throw new Error(
`[${ADDON_NAME}] Canceled: Language setting has been canceled.`,
);
}
}
// Retrieve the list of available languages for source and target, respectively
const availableLocaleSource = deepLGetLanguages('source'); // .map((langObj) => `${langObj.language} (${langObj.name})`).join('\n');
const availableLocaleTarget = deepLGetLanguages('target');
// Prompt user to enter the language settings
// Source language
const promptSourceLocale = ui.prompt(
`In which language is the source text written? Enter the two-letter language code. Leave this value empty to use DeepL's language auto detection.\n\nAvailable values:\n${availableLocaleSource
.map(
(langObj: DeepLSupportedLanguages) =>
`- ${langObj.language} (${langObj.name})`,
)
.join('\n')}`,
ui.ButtonSet.OK_CANCEL,
);
if (promptSourceLocale.getSelectedButton() !== ui.Button.OK) {
throw new Error(
`[${ADDON_NAME}] Canceled: Language setting has been canceled.`,
);
}
const responseSourceLocale = promptSourceLocale
.getResponseText()
.toUpperCase();
if (
!availableLocaleSource
.map((langObj: DeepLSupportedLanguages) => langObj.language)
.includes(responseSourceLocale) &&
responseSourceLocale != ''
) {
throw new Error(
`[${ADDON_NAME}] Invalid Value (${responseSourceLocale}): Enter a valid value.`,
);
}
// Target language
const promptTargetLocale = ui.prompt(
`Into which language should the text be translated? Enter the two- or four-letter language code.\n\nAvailable values:\n${availableLocaleTarget
.map(
(langObj: DeepLSupportedLanguages) =>
`- ${langObj.language} (${langObj.name})`,
)
.join('\n')}`,
ui.ButtonSet.OK_CANCEL,
);
if (promptTargetLocale.getSelectedButton() !== ui.Button.OK) {
throw new Error(
`[${ADDON_NAME}] Canceled: Language setting has been canceled.`,
);
}
const responseTargetLocale = promptTargetLocale
.getResponseText()
.toUpperCase();
if (
!availableLocaleTarget
.map((langObj: DeepLSupportedLanguages) => langObj.language)
.includes(responseTargetLocale)
) {
throw new Error(
`[${ADDON_NAME}] Invalid Value (${responseTargetLocale}): Enter a valid value.`,
);
}
// Set the values as user properties
let setObj: PropertiesObj = {};
const setObj: PropertiesObj = {};

Check warning on line 242 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
setObj[UP_KEY_SOURCE_LOCALE] = responseSourceLocale;
setObj[UP_KEY_TARGET_LOCALE] = responseTargetLocale;
up.setProperties(setObj, false);
Expand Down Expand Up @@ -339,47 +337,46 @@
* @returns Array of translated texts.
* @see https://www.deepl.com/docs-api/translate-text/
*/
export function deepLTranslate(
sourceText: string | string[],
sourceLocale: string | null | undefined,
targetLocale: string,
): string[] {
const endpoint = 'translate';
let sourceTextCasted: string;
if (!sourceText || sourceText.length === 0) {
throw new Error(`[${ADDON_NAME}] Empty input.`);
}
if (Array.isArray(sourceText)) {
sourceTextCasted = sourceText
.map((text) => `text=${encodeURIComponent(text)}`)
.join('&');
} else {
sourceTextCasted = `text=${encodeURIComponent(sourceText)}`;
}
// console.log(`sourceTextCasted: ${sourceTextCasted}`);

// API key
const apiKey = getDeepLApiKey();
const baseUrl = getDeepLApiBaseUrl(apiKey);
// Call the DeepL API
let url =
baseUrl +
endpoint +
`?auth_key=${apiKey}&target_lang=${targetLocale}&${sourceTextCasted}`;
if (sourceLocale) {
url += `&source_lang=${sourceLocale}`;
}
// console.log(`url: ${url}`);

// Call the DeepL API translate request
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });

// Handle DeepL API errors
handleDeepLErrors(response);
const response = handleDeepLErrors(

Check warning on line 373 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
UrlFetchApp.fetch(url, { muteHttpExceptions: true }),

Check warning on line 374 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
);

Check warning on line 375 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement

const translatedTextObj: DeepLTranslationResponse = JSON.parse(
const translatedTextObj = JSON.parse(

Check warning on line 377 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
response.getContentText(),
);
) as DeepLTranslationResponse;

Check warning on line 379 in src/sheetsl.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
const translatedText: string[] = translatedTextObj.translations.map(
(translationsResponse: DeepLTranslationObj): string =>
translationsResponse.text,
Expand All @@ -404,13 +401,12 @@
const apiKey = getDeepLApiKey();
const baseUrl = getDeepLApiBaseUrl(apiKey);
// Call the DeepL API
let url = baseUrl + endpoint + `?auth_key=${apiKey}&type=${type}`;
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });

// Handle DeepL API errors
handleDeepLErrors(response);
const url = baseUrl + endpoint + `?auth_key=${apiKey}&type=${type}`;
const response = handleDeepLErrors(
UrlFetchApp.fetch(url, { muteHttpExceptions: true }),
);

return JSON.parse(response.getContentText());
return JSON.parse(response.getContentText()) as DeepLSupportedLanguages[];
}

/**
Expand All @@ -424,12 +420,13 @@

/**
* Handle DeepL API errors based on the response code.
* Returns the entered response if the response code is 200.
* @param response The UrlFetchApp.fetch response from the DeepL API
* @see https://www.deepl.com/docs-api/api-access/error-handling/
*/
export function handleDeepLErrors(
response: GoogleAppsScript.URL_Fetch.HTTPResponse,
): void {
): GoogleAppsScript.URL_Fetch.HTTPResponse {
const responseCode = response.getResponseCode();
if (responseCode === 429) {
throw new Error(
Expand All @@ -448,6 +445,7 @@
`[${ADDON_NAME}] Error on Calling DeepL API: ${response.getContentText()}`,
);
}
return response;
}

/**
Expand Down
Loading
Loading