-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from Shiou-Ju/feat-13/implement-search-in-kbbi
Feat 13/implement search in kbbi
- Loading branch information
Showing
13 changed files
with
366 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const OPTION_ID = 'searchKBBI' as const; | ||
|
||
chrome.runtime.onInstalled.addListener(() => { | ||
chrome.contextMenus.create({ | ||
id: OPTION_ID, | ||
title: 'Search in KBBI for "%s"', | ||
contexts: ['selection'], | ||
}); | ||
}); | ||
|
||
function isTextEncodable(text: string | undefined): text is string { | ||
return typeof text === 'string' && text.trim().length > 0; | ||
} | ||
|
||
chrome.contextMenus.onClicked.addListener((info, _tab) => { | ||
if (info.menuItemId !== OPTION_ID) return; | ||
|
||
if (isTextEncodable(info.selectionText)) { | ||
const query = encodeURIComponent(info.selectionText); | ||
const searchUrl = `https://kbbi.co.id/cari?kata=${query}`; | ||
chrome.tabs.create({ url: searchUrl }); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
import puppeteer, { Browser, Page } from 'puppeteer'; | ||
import path from 'path'; | ||
import { exec } from 'child_process'; | ||
import { promisify } from 'util'; | ||
|
||
const execAsync = promisify(exec); | ||
|
||
const EXTENSION_PATH = path.join(process.cwd(), 'dist'); | ||
|
||
const LOCAL_WEATHER_URL = 'https://www.cwa.gov.tw/V8/C/W/County/County.html?CID=66'; | ||
|
||
const WIKI_BAHAGIA_PAGE_URL = 'https://en.wiktionary.org/wiki/bahagia'; | ||
|
||
/** | ||
* 嘗試透過 clipboardy 來清空,但是沒辦法 compile | ||
* 接著嘗試使用 jest-clipboard 清空,但遭遇到 transformIgnorePatterns 以及 babel.config.js 設置後,仍然無法運作的問題 | ||
*/ | ||
export async function clearClipboard(page: Page) { | ||
await page.evaluate(() => { | ||
const selection = document.getSelection(); | ||
if (selection) { | ||
selection.removeAllRanges(); | ||
} | ||
|
||
document.execCommand('copy'); | ||
}); | ||
} | ||
|
||
async function rightClickOnElement(page: Page, selector: string) { | ||
const element = await page.$(selector); | ||
|
||
if (!element) throw new Error('no such element'); | ||
|
||
const boundingBox = await element.boundingBox(); | ||
|
||
if (!boundingBox) throw new Error('Element not focused'); | ||
|
||
const middleHeight = boundingBox.x + boundingBox.width / 2; | ||
const middleLength = boundingBox.y + boundingBox.height / 2; | ||
|
||
await page.mouse.click(middleHeight, middleLength, { | ||
button: 'right', | ||
}); | ||
} | ||
|
||
function pause(ms: number) { | ||
return new Promise((resolve) => setTimeout(resolve, ms)); | ||
} | ||
|
||
async function selectText(page: Page, selector: string) { | ||
await page.evaluate((selector) => { | ||
const element = document.querySelector(selector); | ||
if (!element) throw new Error(`Element not found for selector: ${selector}`); | ||
|
||
const range = document.createRange(); | ||
range.selectNodeContents(element); | ||
const selection = window.getSelection(); | ||
if (!selection) throw new Error('No selection object available'); | ||
|
||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
}, selector); | ||
} | ||
|
||
describe('Chrome Browser Context Menu Tests', () => { | ||
let browser: Browser; | ||
let page: Page; | ||
|
||
beforeEach(async () => { | ||
browser = await puppeteer.launch({ | ||
headless: false, | ||
}); | ||
}); | ||
|
||
afterEach(async () => { | ||
await browser.close(); | ||
}); | ||
|
||
test('the keyboard interaction should be successful with puppeteer browser', async () => { | ||
try { | ||
page = await browser.newPage(); | ||
|
||
await page.goto('file:///' + __dirname + '/test-input-page.html'); | ||
|
||
await page.bringToFront(); | ||
|
||
await page.waitForSelector('#testInput'); | ||
|
||
await page.evaluate(() => document.getElementById('testInput')!.focus()); | ||
|
||
const exampleStringToBeAumatedEnter = 'hello world'; | ||
|
||
await execAsync(`python ${__dirname}/scripts/type_text.py "${exampleStringToBeAumatedEnter}"`); | ||
|
||
await pause(2000); | ||
} catch (error) { | ||
console.error(`Error occurred: ${error}`); | ||
throw error; | ||
} | ||
|
||
const inputValue = await page.$eval('#testInput', (el) => (el as HTMLInputElement).value); | ||
|
||
const insertionNoMatterWhichLanguage = inputValue.length; | ||
|
||
const lengthOfHello = 5; | ||
|
||
expect(insertionNoMatterWhichLanguage).toBeGreaterThan(lengthOfHello); | ||
}, 10000); | ||
|
||
test('the second option should copy text', async () => { | ||
await execAsync(`chmod +x ${__dirname}/scripts/type_text.py`); | ||
|
||
await browser! | ||
.defaultBrowserContext() | ||
.overridePermissions('https://www.cwa.gov.tw', ['clipboard-read', 'clipboard-write']); | ||
|
||
page = await browser.newPage(); | ||
|
||
const textSelector = | ||
'body > div.wrapper > main > div > div:nth-child(2) > div.row > div > form > fieldset > div > div:nth-child(1) > div.countryselect-title > label'; | ||
|
||
await page.goto(LOCAL_WEATHER_URL); | ||
await page.waitForSelector(textSelector); | ||
|
||
await selectText(page, textSelector); | ||
|
||
await rightClickOnElement(page, textSelector); | ||
|
||
page.bringToFront(); | ||
|
||
await execAsync(`python ${__dirname}/scripts/choose_copy_context_menu.py`); | ||
|
||
await pause(1000); | ||
|
||
clearClipboard(page); | ||
|
||
const copiedText = await page.evaluate(() => { | ||
return navigator.clipboard.readText(); | ||
}); | ||
|
||
expect(copiedText).toBe('選擇縣市'); | ||
}); | ||
}); | ||
|
||
describe('KBBI Extension Specific', () => { | ||
let browser: Browser; | ||
let page: Page; | ||
|
||
beforeEach(async () => { | ||
browser = await puppeteer.launch({ | ||
headless: false, | ||
args: [`--disable-extensions-except=${EXTENSION_PATH}`, `--load-extension=${EXTENSION_PATH}`], | ||
}); | ||
}); | ||
|
||
afterEach(async () => { | ||
await browser.close(); | ||
}); | ||
|
||
test('new context menu option opens a new tab', async () => { | ||
await execAsync(`chmod +x ${__dirname}/scripts/select_new_option.py`); | ||
|
||
page = await browser.newPage(); | ||
|
||
const textSelector = | ||
'body > div.wrapper > main > div > div:nth-child(2) > div.row > div > form > fieldset > div > div:nth-child(1) > div.countryselect-title > label'; | ||
|
||
await page.goto(LOCAL_WEATHER_URL); | ||
await page.waitForSelector(textSelector); | ||
|
||
await selectText(page, textSelector); | ||
|
||
await rightClickOnElement(page, textSelector); | ||
|
||
await page.bringToFront(); | ||
|
||
await execAsync(`python ${__dirname}/scripts/select_new_option.py`); | ||
|
||
await pause(2000); | ||
|
||
const pages = await browser.pages(); | ||
|
||
const originalPageCount = 2; | ||
const isTabIncreased = pages.length > originalPageCount; | ||
|
||
expect(isTabIncreased).toBe(true); | ||
}, 10000); | ||
|
||
test('Context menu option opens new tab with specific URL and content', async () => { | ||
page = await browser.newPage(); | ||
|
||
await page.goto(WIKI_BAHAGIA_PAGE_URL); | ||
|
||
const textSelector = '#firstHeading > span'; | ||
await page.waitForSelector(textSelector); | ||
|
||
await selectText(page, textSelector); | ||
|
||
await rightClickOnElement(page, textSelector); | ||
|
||
page.bringToFront(); | ||
|
||
await execAsync(`python ${__dirname}/scripts/select_new_option_in_wikipidia.py`); | ||
|
||
await pause(3000); | ||
|
||
const pages = await browser.pages(); | ||
|
||
const countExcludingNewPage = 2; | ||
|
||
expect(pages.length).toBeGreaterThan(countExcludingNewPage); | ||
|
||
const lastOpenedPage = pages.length - 1; | ||
const newTab = pages[lastOpenedPage]; | ||
await newTab.bringToFront(); | ||
|
||
const url = newTab.url(); | ||
|
||
const searchResultUrlRegex = new RegExp('https://kbbi.co.id/cari\\?kata=bahagia'); | ||
|
||
expect(url).toMatch(searchResultUrlRegex); | ||
|
||
const searchResultCountSelector = '#main > div > div.col-sm-9 > div > p:nth-child(4)'; | ||
await newTab.waitForSelector(searchResultCountSelector, { visible: true }); | ||
|
||
const content = await newTab.$eval(searchResultCountSelector, (el) => el.textContent); | ||
expect(content).toBeTruthy(); | ||
}, 45000); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import pyautogui | ||
import time | ||
|
||
|
||
def press_key(key, presses=1, interval=0.3): | ||
for _ in range(presses): | ||
pyautogui.press(key) | ||
time.sleep(interval) | ||
|
||
|
||
# Press 'ArrowDown' twice and 'Enter' once | ||
press_key('down', presses=2) | ||
press_key('enter', presses=1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import pyautogui | ||
import time | ||
|
||
|
||
def press_key(key, presses=1, interval=0.3): | ||
for _ in range(presses): | ||
pyautogui.press(key) | ||
time.sleep(interval) | ||
|
||
|
||
steps_before_inspect = 5 | ||
|
||
press_key('down', presses=steps_before_inspect) | ||
press_key('enter', presses=1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import pyautogui | ||
import time | ||
|
||
|
||
def press_key(key, presses=1, interval=0.3): | ||
for _ in range(presses): | ||
pyautogui.press(key) | ||
time.sleep(interval) | ||
|
||
|
||
steps_before_inspect = 6 | ||
|
||
press_key('down', presses=steps_before_inspect) | ||
press_key('enter', presses=1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# if the code does not work, take macos for example, open the System Preferences > Security & Privacy > Privacy tab. | ||
# turn on permission for the terminal app, and execute this try to see if spotlight is open | ||
# pyautogui.hotkey('command', 'space', interval=0.1) | ||
|
||
import pyautogui | ||
import sys | ||
|
||
|
||
# even using mandarin or other languages, as long as there is insertion, the test shall pass | ||
|
||
# import time | ||
|
||
# change insertion if using multiple keyboard | ||
# pyautogui.hotkey('ctrl', 'space', interval=0.2) | ||
# time.sleep(0.3) | ||
# pyautogui.hotkey('ctrl', 'space', interval=0.2) | ||
# time.sleep(0.3) | ||
# pyautogui.press('capslock') | ||
# time.sleep(0.3) | ||
|
||
|
||
text_to_type = sys.argv[1] if len(sys.argv) > 1 else "hello world" | ||
pyautogui.write(text_to_type) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Test Page</title> | ||
</head> | ||
<body> | ||
<input type="text" id="testInput" /> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.