diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..d327318 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,15 @@ +version = 1 + +test_patterns = ["**/*.test.js"] + +[[analyzers]] +name = "secrets" +enabled = true + +[[analyzers]] +name = "test-coverage" +enabled = true + +[[analyzers]] +name = "javascript" +enabled = true diff --git a/.eslintrc b/.eslintrc index afcc776..5ea4e0e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,10 +1,10 @@ { "env": { - "node": true + "browser": true, + "es2021": true }, "extends": "airbnb-base", "parserOptions": { - "ecmaVersion": 6, "sourceType": "module" } } diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1041dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,208 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..36c962b --- /dev/null +++ b/jest.config.js @@ -0,0 +1,195 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/5j/dn6r35sd781312lr0cdj1fqh0000gn/T/jest_dx", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "jsdom", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/js/background.js b/js/background.js new file mode 100644 index 0000000..26ab34b --- /dev/null +++ b/js/background.js @@ -0,0 +1,10 @@ +/* Lorsque l'extension Chrome est installé, ouvre la documentation de l'extension */ +chrome.runtime.onInstalled.addListener(function() { + chrome.tabs.create({ + url: "https://github.com/nitatemic/ReplikaWithDeepL/wiki/You-just-installed-the-extension-and-now%3F" + }); + /* Définir l'endpoint par défaut */ + chrome.storage.sync.set({ + "endpoint": "https://api.deepl.com/v2" + }); +}); diff --git a/js/popup.js b/js/popup.js new file mode 100644 index 0000000..eaf3abc --- /dev/null +++ b/js/popup.js @@ -0,0 +1,4 @@ +const ChooseEndpoint = require("./popup/chooseEndpoint"); +const getAPIKEY = require("./popup/getAPIKEY"); +const testAPIKEY = require("./popup/testAPIKEY"); +const sendTestRequest = require("./popup/sendTestRequest"); diff --git a/js/popup/__tests__/getAPIKEY.test.js b/js/popup/__tests__/getAPIKEY.test.js new file mode 100644 index 0000000..e046cc6 --- /dev/null +++ b/js/popup/__tests__/getAPIKEY.test.js @@ -0,0 +1,12 @@ +require('dotenv/config'); +const getAPIKEY = require('../getAPIKEY'); +const API_KEY_TEST = process.env.API_KEY_TEST; + +test(`Return the API key`, () => { + /* Set the API key in the input field */ + document.body.innerHTML = + ``; + + /* Check the value of the API key */ + expect(getAPIKEY()).toBe(API_KEY_TEST); + }); diff --git a/js/popup/__tests__/sendTestRequest.test.js b/js/popup/__tests__/sendTestRequest.test.js new file mode 100644 index 0000000..8876243 --- /dev/null +++ b/js/popup/__tests__/sendTestRequest.test.js @@ -0,0 +1,8 @@ +require('dotenv/config'); +const sendTestRequest = require('../sendTestRequest'); +const API_KEY_TEST = process.env.API_KEY_TEST; + +test(`Error Handling in sendTestRequest`, () => { + /* Check the value of the API key */ + expect(sendTestRequest()).toEqual( "Fetch error"); +}); diff --git a/js/popup/__tests__/testAPIKEY.test.js b/js/popup/__tests__/testAPIKEY.test.js new file mode 100644 index 0000000..8f00147 --- /dev/null +++ b/js/popup/__tests__/testAPIKEY.test.js @@ -0,0 +1,8 @@ +require('dotenv/config'); +const testAPIKEY = require('../testAPIKEY'); +const API_KEY_TEST = process.env.API_KEY_TEST; + +test(`Error Handling in testAPIKEY`, () => { + /* Check the value of the API key */ + expect(testAPIKEY()).toEqual({"message": "Fetch error", "status": "error"}); +}); diff --git a/js/popup/chooseEndpoint.js b/js/popup/chooseEndpoint.js new file mode 100644 index 0000000..723f0c5 --- /dev/null +++ b/js/popup/chooseEndpoint.js @@ -0,0 +1,19 @@ +const sendTestRequest = require("./sendTestRequest"); + +/** + * It sends a test request to the server, parses the response, and if the response is "Wrong endpoint. + * Use https://api-free.deepl.com", it saves the endpoint in the extension's storage + * @param APIKEY - The API key you got from DeepL. + */ +function ChooseEndpoint(APIKEY) { + let request = sendTestRequest(APIKEY); + /* Parsing the response from the server into a JavaScript object. */ + let response = JSON.parse(request.response); + if (response.message === "Wrong endpoint. Use https://api-free.deepl.com") { + /*Enregistre l'endpoint dans le storage de l'extension*/ + chrome.storage.sync.set({ + "endpoint": "https://api-free.deepl.com/v2" + }); + } +} +module.exports = ChooseEndpoint; diff --git a/js/popup/getAPIKEY.js b/js/popup/getAPIKEY.js new file mode 100644 index 0000000..82a2502 --- /dev/null +++ b/js/popup/getAPIKEY.js @@ -0,0 +1,10 @@ +/** + * The function `getAPIKEY()` is called when the user clicks the "Submit" button. The function then + * takes the value of the text box and assigns it to the variable `API_KEY` + */ + +function getAPIKEY() { + return document.getElementById("API_KEY").value; +} + +module.exports = getAPIKEY; diff --git a/js/popup/sendTestRequest.js b/js/popup/sendTestRequest.js new file mode 100644 index 0000000..e8cca6a --- /dev/null +++ b/js/popup/sendTestRequest.js @@ -0,0 +1,27 @@ +function sendTestRequest(APIKEY) { + /* Get endpoint from storage */ + let endpoint; + try { + chrome.storage.sync.get("endpoint", function(result) { + endpoint = result.endpoint; + }); + } + catch (e) { + endpoint = "https://api.deepl.com/v2"; + } + + + /* Trying to fetch the endpoint and return the response. If it fails, it returns 'Fetch error'. */ + try { + fetch(`${endpoint}usage?auth_key=${APIKEY}`) + .then(response => { + return response + }); + } + catch (e) { + return 'Fetch error'; + } + +} + +module.exports = sendTestRequest; diff --git a/js/popup/testAPIKEY.js b/js/popup/testAPIKEY.js new file mode 100644 index 0000000..bd02a68 --- /dev/null +++ b/js/popup/testAPIKEY.js @@ -0,0 +1,49 @@ +const sendTestRequest = require("./sendTestRequest"); +function testAPIKEY(API_KEY) { + + if (API_KEY === "") { + return { + "status": "error", + "message": "Please enter your API key" + }; + } + + let request = sendTestRequest(API_KEY); + if (request === "Fetch error") { + return { + "status": "error", + "message": "Fetch error" + }; + } + /* Parsing the response from the server into a JavaScript object. */ + let response = JSON.parse(request.body); + + switch (request.status) { + case 200: + return { + "status": "success", + "message": "Your API key is valid" + } + + case 456: + return { + "status": "error", + "message": "Your API key is valid but you have reached the limit of your account" + } + + case 500: + return { + "status": "error", + "message": "DeepL seems to be down. Please try again later" + } + + default: + console.error(response.message); + return { + "status": "error", + "message": "Check the console for more information" + } + } +} + +module.exports = testAPIKEY; diff --git a/legacy/DeepL API test.http b/legacy/DeepL API test.http new file mode 100644 index 0000000..e69de29 diff --git a/replikaWithDeepl.js b/legacy/replikaWithDeepl.js similarity index 100% rename from replikaWithDeepl.js rename to legacy/replikaWithDeepl.js diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..a27773f --- /dev/null +++ b/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "Replika with DeepL", + "description": "Talk with your Replika in your native language", + "version": "1.0", + "manifest_version": 3, + "background": { + "service_worker": "./js/background.js" + }, + "permissions": ["storage"], + "action": { + "default_popup": "popup.html" + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..dfc7c08 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "ReplikaWithDeepL", + "version": "1.0.0", + "main": "index.js", + "repository": "https://github.com/nitatemic/ReplikaWithDeepL.git", + "author": "nitatemic ", + "license": "AGPL-3.0-or-later", + "devDependencies": { + "eslint": "^8.17.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.26.0", + "jest": "^28.1.1", + "jest-environment-jsdom": "^28.1.1" + }, + "dependencies": { + "dotenv": "^16.0.1" + }, + "scripts": { + "test": "jest" + } +} diff --git a/popup.html b/popup.html new file mode 100644 index 0000000..9df8725 --- /dev/null +++ b/popup.html @@ -0,0 +1,22 @@ + + + + + Replika with DeepL + + +

Your DeepL API key

+ +

Mode

+ +