Skip to content

Commit

Permalink
Merge pull request #3 from mareanalitica/feature/puppeteer-handler-an…
Browse files Browse the repository at this point in the history
…d-JSdocs

feat: Add PuppeteerHandler class for common Puppeteer operations
  • Loading branch information
jhowbhz committed Feb 6, 2024
2 parents 1eb3bbf + 300e21a commit 93a8003
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 21 deletions.
121 changes: 121 additions & 0 deletions src/utils/puppeteerHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import puppeteer, { Browser, Page, ElementHandle } from 'puppeteer';

/**
* An abstract class to handle common Puppeteer operations.
*/
export abstract class PuppeteerHandler {
private browser: Browser | null = null;
private page: Page | null = null;

/**
* Opens a new Puppeteer browser instance.
*/
protected async openBrowser() {
this.browser = await puppeteer.launch({
headless: process.env.NODE_ENV === 'production' ? 'new' : false,
slowMo: process.env.NODE_ENV === 'production' ? 0 : 50,
timeout: 10000,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
],
});
}

/**
* Opens a new page in the current browser instance and navigates to the specified URL.
* @param {string} url - The URL to navigate to.
*/
protected async openPage(url: string) {
if (!this.browser) {
throw new Error('Browser is not open');
}

this.page = await this.browser.newPage();
await this.page.goto(url, { waitUntil: 'networkidle2', timeout: 10000 });
}

/**
* Closes the Puppeteer browser instance.
*/
protected async closeBrowser() {
if (this.browser) {
await this.browser.close();
this.browser = null;
}
}

/**
* Closes the current page in the browser.
*/
protected async closePage() {
if (this.page) {
await this.page.close();
this.page = null;
}
}

/**
* Waits for an element matching the given selector to appear in the current page.
* @param {string} selector - The selector to wait for.
*/
protected async waitForSelector(selector: string) {
if (!this.page) {
throw new Error('Page is not open');
}

await this.page.waitForSelector(selector);
}

/**
* Clicks on an element with the specified selector.
* @param {string} selector - The selector of the element to click.
*/
protected async click(selector: string) {
if (!this.page) {
throw new Error('Page is not open');
}

await this.page.click(selector);
}

/**
* Gets an element with the specified selector.
* @param {string} selector - The selector of the element to get.
* @returns {Promise<ElementHandle<any> | null>} - A Promise that resolves with the ElementHandle or null if not found.
*/
protected async getElement(selector: string): Promise<ElementHandle<any> | null> {
if (!this.page) {
throw new Error('Page is not open');
}

return this.page.$(selector);
}

/**
* Gets all elements matching the specified selector.
* @param {string} selector - The selector of the elements to get.
* @returns {Promise<ElementHandle<any>[]>} - A Promise that resolves with an array of ElementHandles.
*/
protected async getElements(selector: string): Promise<ElementHandle<any>[]> {
if (!this.page) {
throw new Error('Page is not open');
}

return this.page.$$(selector);
}

/**
* Types the specified text into an element with the given selector.
* @param {string} selector - The selector of the input element.
* @param {string} text - The text to type.
*/
protected async type(selector: string, text: string) {
const element = await this.getElement(selector);
if (element) {
await element.type(text);
}
}
}
82 changes: 61 additions & 21 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,67 @@
import axios from 'axios';
class Utils {

imageFileToBase64 = async (image: any) => {
class Utils {
/**
* Converts an image file to base64 encoding.
* @param {any} image - The image to convert.
* @returns {Promise<string>} - The base64-encoded image.
*/
async imageFileToBase64(image) {
const file = await image.screenshot({ encoding: "base64" });
return file;
}

sleep = (ms: number) => {
/**
* Sleeps for a specified number of milliseconds.
* @param {number} ms - The number of milliseconds to sleep.
* @returns {Promise<void>} - A Promise that resolves after sleeping.
*/
sleep(ms) {
console.log(`Sleeping for ${ms}ms`);
return new Promise(resolve => setTimeout(resolve, ms));
}

clearString = (value: string) => {
/**
* Clears unwanted characters and whitespace from a string.
* @param {string} value - The input string to clear.
* @returns {string} - The cleaned string.
*/
clearString(value) {
const stringClear = value.replace(/(<([^>]+)>)/gi, "").replace(/(\r\n|\n|\r)/gm, "").replace(/\s+/g, " ").trim();
return stringClear;
}

convertStringToDecimal = (value: string) => {
/**
* Converts a formatted currency string to a decimal number.
* @param {string} value - The currency string to convert.
* @returns {number} - The decimal representation of the currency.
*/
convertStringToDecimal(value) {
return Number(value.replace('R$', '').replace('.', '').replace(',', '.').replace('R$ ', ''));
}

removeAccents(str: string) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}

webhook = async (url: string, method?: string, header?: any, body?: any) => {

this.request(url, method ? method : 'POST', header ? header : {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Detran-PI Scraper'
}, body ? body : {});

/**
* Removes diacritics (accents) from a string.
* @param {string} str - The input string with diacritics.
* @returns {string} - The string with diacritics removed.
*/
removeAccents(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}

request = async (url: string, method: string, headers: any, body: any) => {

/**
* Sends an HTTP request using Axios.
* @param {string} url - The URL to send the request to.
* @param {string} [method='POST'] - The HTTP method (default is POST).
* @param {object} [header] - The request headers.
* @param {object} [body] - The request body.
* @returns {Promise<any>} - A Promise that resolves with the response data.
*/
async request(url, method = 'POST', header = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Detran-PI Scraper'
}, body = {}) {
const response = await axios.request({
url,
method,
Expand All @@ -44,9 +70,23 @@ class Utils {
});

return response.data;

}

/**
* Sends an HTTP request to a specified URL using the provided method, headers, and body.
* @param {string} url - The URL to send the request to.
* @param {string} [method] - The HTTP method (default is POST).
* @param {object} [header] - The request headers.
* @param {object} [body] - The request body.
* @returns {Promise<void>}
*/
async webhook(url, method, header, body) {
this.request(url, method || 'POST', header || {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'Detran-PI Scraper'
}, body || {});
}
}

export default new Utils()
export default new Utils();

0 comments on commit 93a8003

Please sign in to comment.