From e6a99411b6dc1c364f33d92fc9c3056f73ca771e Mon Sep 17 00:00:00 2001 From: ascariandrea Date: Thu, 15 Sep 2022 12:39:46 +0200 Subject: [PATCH 1/2] test(shared): read parser fixture content once at time --- .../shared/src/providers/parser.provider.ts | 12 +- .../shared/src/test/utils/parser.utils.ts | 62 +++++--- .../backend/parsers/__tests__/native.e2e.ts | 14 +- .../__tests__/parser/html/parseHome.e2e.ts | 21 ++- .../__tests__/parser/html/parseSearch.e2e.ts | 149 +++++++++-------- .../__tests__/parser/html/parseVideo.e2e.ts | 150 +++++++++--------- .../__tests__/parser/leaf/parseLeaf.e2e.ts | 19 ++- 7 files changed, 232 insertions(+), 195 deletions(-) diff --git a/packages/shared/src/providers/parser.provider.ts b/packages/shared/src/providers/parser.provider.ts index 3f71e141d..07b01fcd5 100644 --- a/packages/shared/src/providers/parser.provider.ts +++ b/packages/shared/src/providers/parser.provider.ts @@ -311,7 +311,10 @@ const pipeline = >( ctx: ParserContext ) => - async (e: T, parsers: PP): Promise> => { + async ( + e: t.TypeOf, + parsers: PP + ): Promise, PP>> => { let results: PipelineInput = { failures: {}, source: e, @@ -401,7 +404,7 @@ export const parseContributions = const results: Array, t.TypeOf, PP>> = []; - ctx.log.debug('Sources %O', envelops.sources); + ctx.log.debug('Sources %O', envelops.sources.map(ctx.getEntryId)); for (const entry of envelops.sources) { ctx.log.debug('Parsing entry %O', ctx.getEntryId(entry)); @@ -513,7 +516,10 @@ export const executionLoop = } else { ctx.log.debug('Data to process %d', envelops.sources.length); const currentResult = await parseContributions(ctx)(envelops); - ctx.log.debug('Processed envelops %O', currentResult); + ctx.log.debug( + 'Processed sources %O', + currentResult.map((r) => ctx.getEntryId(r.source)) + ); results.push(...currentResult); } diff --git a/packages/shared/src/test/utils/parser.utils.ts b/packages/shared/src/test/utils/parser.utils.ts index 4d6485070..ac0e24bdf 100644 --- a/packages/shared/src/test/utils/parser.utils.ts +++ b/packages/shared/src/test/utils/parser.utils.ts @@ -2,38 +2,52 @@ import * as E from 'fp-ts/lib/Either'; import * as fs from 'fs'; import * as t from 'io-ts'; import { PathReporter } from 'io-ts/lib/PathReporter'; -import * as path from 'path'; +import path from 'path'; import { Logger } from '../../logger'; import { - parseContributions, ParserFn, ParserProviderContext + parseContributions, + ParserFn, + ParserProviderContext, } from '../../providers/parser.provider'; /** - * Read the parsed metadata history from '../fixtures/${nature}' + * Read fixtures file from path + * + * @param fixtureDir + * @returns string */ -export const readHistoryResults = ( - fixtureDir: string, - publicKey: string -): any[] => { +export const readFixtureJSONPaths = (fixtureDir: string): any[] => { return fs .readdirSync(fixtureDir, 'utf-8') .filter((file) => file.includes('.json')) - .map((file) => { - const filePath = path.resolve(fixtureDir, file); - // eslint-disable-next-line - console.log('reading content from ', filePath); - const content = fs.readFileSync(filePath, 'utf-8'); - const contentJSON = JSON.parse(content); - return contentJSON; - }) - .map((mt) => ({ - ...mt, - sources: mt.sources.map((h: any) => ({ - ...h, - publicKey, - })), - metadata: { ...mt.metadata, publicKey }, - })); + .map((fp) => path.resolve(fixtureDir, fp)); +}; + +/** + * Read the parsed metadata history from '../fixtures/${nature}' + * + * @param fixtureFilePath + * @param publicKey Supporter public key + */ +export const readFixtureJSON = ( + fixtureFilePath: string, + publicKey: string +): { sources: any[]; metadata: any } => { + // eslint-disable-next-line + console.log( + 'reading content from ', + path.relative(process.cwd(), fixtureFilePath) + ); + const content = fs.readFileSync(fixtureFilePath, 'utf-8'); + const mt = JSON.parse(content); + return { + ...mt, + sources: mt.sources.map((h: any) => ({ + ...h, + publicKey, + })), + metadata: { ...mt.metadata, publicKey }, + }; }; // type MetadataResult = T & { sources: S[]; _id: string }; @@ -62,7 +76,7 @@ export const runParserTest = expectSources: (s: Array>) => void; } & ParserProviderContext) => async ({ sources, metadata }: any) => { - opts.log.debug('Sources %d', sources.length); + // opts.log.debug('Sources %d', sources.length); // insert the sources in the db await opts.db.api.insertMany( diff --git a/platforms/tktrex/backend/parsers/__tests__/native.e2e.ts b/platforms/tktrex/backend/parsers/__tests__/native.e2e.ts index a62555c57..7900987a4 100644 --- a/platforms/tktrex/backend/parsers/__tests__/native.e2e.ts +++ b/platforms/tktrex/backend/parsers/__tests__/native.e2e.ts @@ -1,5 +1,6 @@ import { - readHistoryResults, + readFixtureJSON, + readFixtureJSONPaths, runParserTest, } from '@shared/test/utils/parser.utils'; import { sanitizeHTML } from '@shared/utils/html.utils'; @@ -48,14 +49,17 @@ describe('Parser: "native"', () => { describe('Native', () => { jest.setTimeout(20 * 1000); - const history = readHistoryResults( - path.resolve(__dirname, 'fixtures/native'), - publicKey + const history = readFixtureJSONPaths( + path.resolve(__dirname, 'fixtures/native') ); test.each(history)( 'Should correctly parse "native" contribution', - async ({ sources: _sources, metadata }) => { + async (fixturePath) => { + const { sources: _sources, metadata } = readFixtureJSON( + fixturePath, + publicKey + ); const sources = _sources.map((s: any) => ({ html: { ...s, diff --git a/platforms/yttrex/backend/__tests__/parser/html/parseHome.e2e.ts b/platforms/yttrex/backend/__tests__/parser/html/parseHome.e2e.ts index 8a3a4a5d4..ca29670ee 100644 --- a/platforms/yttrex/backend/__tests__/parser/html/parseHome.e2e.ts +++ b/platforms/yttrex/backend/__tests__/parser/html/parseHome.e2e.ts @@ -1,5 +1,6 @@ import { - readHistoryResults, + readFixtureJSON, + readFixtureJSONPaths, runParserTest, } from '@shared/test/utils/parser.utils'; import { sanitizeHTML } from '@shared/utils/html.utils'; @@ -18,7 +19,7 @@ import { import processHome from '../../../parsers/home'; import { GetTest, Test } from '../../../tests/Test'; -describe('Parserv', () => { +describe('Parser: home', () => { let appTest: Test; const newKeypair = nacl.sign.keyPair(); const publicKey = base58.encode(newKeypair.publicKey); @@ -57,14 +58,18 @@ describe('Parserv', () => { ); }); - const history = readHistoryResults( - path.resolve(__dirname, '../../fixtures/home'), - publicKey - ).filter((v, i) => ![5, 9].includes(i)); + const history = readFixtureJSONPaths( + path.resolve(__dirname, '../../fixtures/home') + ) + // .filter((v, i) => ![5, 9].includes(i)); test.each([history[0]])( 'Should correctly parse home contributions', - async ({ sources: _sources, metadata }) => { + async (filePath) => { + const { sources: _sources, metadata } = readFixtureJSON( + filePath, + publicKey + ); const sources = _sources.map((h: any) => ({ html: { ...h, @@ -76,7 +81,7 @@ describe('Parserv', () => { jsdom: new JSDOM(sanitizeHTML(h.html)).window.document, })); - const result = await runParserTest({ + await runParserTest({ log: appTest.logger, sourceSchema: appTest.config.get('schema').htmls, metadataSchema: appTest.config.get('schema').metadata, diff --git a/platforms/yttrex/backend/__tests__/parser/html/parseSearch.e2e.ts b/platforms/yttrex/backend/__tests__/parser/html/parseSearch.e2e.ts index 331dc27ae..032a3770d 100644 --- a/platforms/yttrex/backend/__tests__/parser/html/parseSearch.e2e.ts +++ b/platforms/yttrex/backend/__tests__/parser/html/parseSearch.e2e.ts @@ -13,7 +13,8 @@ import { sanitizeHTML } from '@shared/utils/html.utils'; import { processSearch } from '../../../parsers/searches'; import { GetTest, Test } from '../../../tests/Test'; import { - readHistoryResults, + readFixtureJSON, + readFixtureJSONPaths, runParserTest, } from '@shared/test/utils/parser.utils'; import path from 'path'; @@ -58,9 +59,8 @@ describe('Parser: Search', () => { jest.setTimeout(20 * 1000); - const historyData = readHistoryResults( - path.resolve(__dirname, '../../fixtures/search'), - publicKey + const historyData = readFixtureJSONPaths( + path.resolve(__dirname, '../../fixtures/search') ); /** @@ -77,82 +77,81 @@ describe('Parser: Search', () => { test.each([ historyData[0], historyData[2], - // historyData[4], historyData[5], - // historyData[6], historyData[11], - ])( - 'Should correctly parse video contributions', - async ({ sources: _sources, metadata }) => { - const sources = _sources.map((h: any) => ({ - html: { - ...h, - clientTime: parseISO(h.clientTime ?? new Date()), - savingTime: subMinutes(new Date(), 1), - processed: null, - }, - jsdom: new JSDOM(sanitizeHTML(h.html)).window.document, - supporter: undefined, - })); + ])('Should correctly parse video contributions', async (fixturePath) => { + const { sources: _sources, metadata } = readFixtureJSON( + fixturePath, + publicKey + ); + const sources = _sources.map((h: any) => ({ + html: { + ...h, + clientTime: parseISO(h.clientTime ?? new Date()), + savingTime: subMinutes(new Date(), 1), + processed: null, + }, + jsdom: new JSDOM(sanitizeHTML(h.html)).window.document, + supporter: undefined, + })); - await runParserTest({ - log: appTest.logger, - parsers: { nature: processSearch }, - codecs: { contribution: HTMLSource, metadata: SearchMetadata }, - sourceSchema: appTest.config.get('schema').htmls, - metadataSchema: appTest.config.get('schema').metadata, - db, - getEntryId: (e) => e.html.id, - getEntryDate: (e) => e.html.savingTime, - getEntryNatureType: (e) => e.html.nature.type, - getContributions: getLastHTMLs(db), - buildMetadata: toMetadata as any, - saveResults: updateMetadataAndMarkHTML(db) as any, - expectSources: (sources) => { - sources.forEach((r: any) => { - expect(r.processed).toBe(true); - }); - }, + await runParserTest({ + log: appTest.logger, + parsers: { nature: processSearch }, + codecs: { contribution: HTMLSource, metadata: SearchMetadata }, + sourceSchema: appTest.config.get('schema').htmls, + metadataSchema: appTest.config.get('schema').metadata, + db, + getEntryId: (e) => e.html.id, + getEntryDate: (e) => e.html.savingTime, + getEntryNatureType: (e) => e.html.nature.type, + getContributions: getLastHTMLs(db), + buildMetadata: toMetadata as any, + saveResults: updateMetadataAndMarkHTML(db) as any, + expectSources: (sources) => { + sources.forEach((r: any) => { + expect(r.processed).toBe(true); + }); + }, - expectMetadata: (metadata, updatedMetadata) => { - const { - savingTime: _savingTime, - clientTime: _clientTimeM, - _id, - id, - results: _receivedResults, - ...receivedMetadata - } = metadata; + expectMetadata: (metadata, updatedMetadata) => { + const { + savingTime: _savingTime, + clientTime: _clientTimeM, + _id, + id, + results: _receivedResults, + ...receivedMetadata + } = metadata; - const { - htmls: _htmls, - clientTime: _clientTimeNewM, - results: _expectedResults, - savingTime: _expectedSavingTime, - id: _expectedId, - _id: _expected_Id, - ...expectedMetadata - } = updatedMetadata as any; + const { + htmls: _htmls, + clientTime: _clientTimeNewM, + results: _expectedResults, + savingTime: _expectedSavingTime, + id: _expectedId, + _id: _expected_Id, + ...expectedMetadata + } = updatedMetadata as any; - expect({ - ...receivedMetadata, - }).toMatchObject({ - ...expectedMetadata, - }); + expect({ + ...receivedMetadata, + }).toMatchObject({ + ...expectedMetadata, + }); - expect( - _receivedResults.map(({ secondsAgo, ...r }: any) => ({ - ...r, - published: r.published - .replace(/\d{1}\s(year)$/gi, 'a year') - .replace(/\d{1}\s(month)$/gi, 'a month') - .replace(/\d{1}\s(day)$/gi, 'a day') - .replace(/\d{1}\s(hour)$/gi, 'an hour') - .replace(/\d{1}\s(minute)$/gi, 'a minute'), - })) - ).toMatchObject(_expectedResults.map(({ secondsAgo, ...r }) => r)); - }, - })({ sources, metadata }); - } - ); + expect( + _receivedResults.map(({ secondsAgo, ...r }: any) => ({ + ...r, + published: r.published + .replace(/\d{1}\s(year)$/gi, 'a year') + .replace(/\d{1}\s(month)$/gi, 'a month') + .replace(/\d{1}\s(day)$/gi, 'a day') + .replace(/\d{1}\s(hour)$/gi, 'an hour') + .replace(/\d{1}\s(minute)$/gi, 'a minute'), + })) + ).toMatchObject(_expectedResults.map(({ secondsAgo, ...r }) => r)); + }, + })({ sources, metadata }); + }); }); diff --git a/platforms/yttrex/backend/__tests__/parser/html/parseVideo.e2e.ts b/platforms/yttrex/backend/__tests__/parser/html/parseVideo.e2e.ts index 5cee64265..2ff511223 100644 --- a/platforms/yttrex/backend/__tests__/parser/html/parseVideo.e2e.ts +++ b/platforms/yttrex/backend/__tests__/parser/html/parseVideo.e2e.ts @@ -10,10 +10,11 @@ import { toMetadata, updateMetadataAndMarkHTML, } from '../../../lib/parser/html'; -import parseVideo from '../../../parsers/video'; +import parseVideo from '../../../parsers/video'; import { GetTest, Test } from '../../../tests/Test'; import { - readHistoryResults, + readFixtureJSON, + readFixtureJSONPaths, runParserTest, } from '@shared/test/utils/parser.utils'; import path from 'path'; @@ -58,9 +59,8 @@ describe('Parser: Video', () => { jest.useRealTimers(); jest.setTimeout(20 * 1000); - const historyData = readHistoryResults( - path.resolve(__dirname, '../../fixtures/video'), - publicKey + const historyData = readFixtureJSONPaths( + path.resolve(__dirname, '../../fixtures/video') ); /** @@ -82,77 +82,79 @@ describe('Parser: Video', () => { // historyData[6] // historyData[7] historyData[8], - ])( - 'Should correctly parse video contributions', - async ({ sources: _sources, metadata }) => { - const sources = _sources.map((h: any) => ({ - html: { - ...h, - clientTime: parseISO(h.clientTime ?? new Date().toISOString()), - savingTime: addMinutes(new Date(), 1), - processed: null, - }, - jsdom: new JSDOM(sanitizeHTML(h.html)).window.document, - supporter: undefined, - })); + ])('Should correctly parse video contributions', async (fixturePath) => { + const { sources: _sources, metadata } = readFixtureJSON( + fixturePath, + publicKey + ); - await runParserTest({ - log: appTest.logger, - db, - sourceSchema: appTest.config.get('schema').htmls, - metadataSchema: appTest.config.get('schema').metadata, - parsers: { nature: parseVideo }, - codecs: { contribution: HTMLSource, metadata: VideoMetadata }, - getEntryId: (e) => e.html.id, - getEntryDate: (e) => e.html.savingTime, - getEntryNatureType: (e) => e.html.nature.type ?? e.type, - getContributions: getLastHTMLs(db), - buildMetadata: toMetadata as any, - saveResults: updateMetadataAndMarkHTML(db), - expectSources: (s) => { - s.forEach((r: any) => { - expect(r.processed).toBe(true); - }); - }, - expectMetadata: (receivedM: any, expectedM: any) => { - const { - related: receivedRelated, - // login: receivedLogin, - likeInfo, - ...receivedMetadata - } = receivedM; + const sources = _sources.map((h: any) => ({ + html: { + ...h, + clientTime: parseISO(h.clientTime ?? new Date().toISOString()), + savingTime: addMinutes(new Date(), 1), + processed: null, + }, + jsdom: new JSDOM(sanitizeHTML(h.html)).window.document, + supporter: undefined, + })); - const { - savingTime: _savingTime, - clientTime: _clientTime, - _id, - id, - // login: expectedLogin, - related: expectedRelated, - likeInfo: expectedLikeInfo, - ...expectedMetadata - } = expectedM; + await runParserTest({ + log: appTest.logger, + db, + sourceSchema: appTest.config.get('schema').htmls, + metadataSchema: appTest.config.get('schema').metadata, + parsers: { nature: parseVideo }, + codecs: { contribution: HTMLSource, metadata: VideoMetadata }, + getEntryId: (e) => e.html.id, + getEntryDate: (e) => e.html.savingTime, + getEntryNatureType: (e) => e.html.nature.type ?? e.type, + getContributions: getLastHTMLs(db), + buildMetadata: toMetadata as any, + saveResults: updateMetadataAndMarkHTML(db), + expectSources: (s) => { + s.forEach((r: any) => { + expect(r.processed).toBe(true); + }); + }, + expectMetadata: (receivedM: any, expectedM: any) => { + const { + related: receivedRelated, + // login: receivedLogin, + likeInfo, + ...receivedMetadata + } = receivedM; - expect({ - ...receivedMetadata, - publicationTime: receivedMetadata?.publicationTime?.toISOString(), - }).toMatchObject({ - ...expectedMetadata, - }); + const { + savingTime: _savingTime, + clientTime: _clientTime, + _id, + id, + // login: expectedLogin, + related: expectedRelated, + likeInfo: expectedLikeInfo, + ...expectedMetadata + } = expectedM; - // check metadata related - expect( - receivedRelated.map( - ({ recommendedPubTime, publicationTime, ...rr }: any) => ({ - ...rr, - foryou: rr.foryou ?? null, - }) - ) - ).toMatchObject( - expectedRelated.map(({ publicationTime, ...rr }: any) => rr) - ); - }, - })({ metadata, sources }); - } - ); + expect({ + ...receivedMetadata, + publicationTime: receivedMetadata?.publicationTime?.toISOString(), + }).toMatchObject({ + ...expectedMetadata, + }); + + // check metadata related + expect( + receivedRelated.map( + ({ recommendedPubTime, publicationTime, ...rr }: any) => ({ + ...rr, + foryou: rr.foryou ?? null, + }) + ) + ).toMatchObject( + expectedRelated.map(({ publicationTime, ...rr }: any) => rr) + ); + }, + })({ metadata, sources }); + }); }); diff --git a/platforms/yttrex/backend/__tests__/parser/leaf/parseLeaf.e2e.ts b/platforms/yttrex/backend/__tests__/parser/leaf/parseLeaf.e2e.ts index 97fb8755d..167eb4762 100644 --- a/platforms/yttrex/backend/__tests__/parser/leaf/parseLeaf.e2e.ts +++ b/platforms/yttrex/backend/__tests__/parser/leaf/parseLeaf.e2e.ts @@ -2,7 +2,7 @@ import base58 from 'bs58'; import nacl from 'tweetnacl'; import { Ad } from '@yttrex/shared/models/Ad'; import { sanitizeHTML } from '@shared/utils/html.utils'; -import { processLeaf } from '../../../parsers/leaf'; +import { leafParsers } from '../../../parsers'; import { getLastLeaves, LeafSource, @@ -11,7 +11,8 @@ import { } from '../../../lib/parser/leaf'; import { GetTest, Test } from '../../../tests/Test'; import { - readHistoryResults, + readFixtureJSON, + readFixtureJSONPaths, runParserTest, } from '@shared/test/utils/parser.utils'; import { parseISO, subMinutes } from 'date-fns'; @@ -36,22 +37,26 @@ describe('Leaves parser', () => { describe('Leaves', () => { jest.setTimeout(20 * 1000); - const history = readHistoryResults( + const history = readFixtureJSONPaths( path.resolve(__dirname, '../../fixtures/leaves/home'), publicKey ); test.each(history)( 'Should correctly parse leaf contribution', - async ({ sources: _sources, metadata }) => { + async (fixturePath) => { + const { sources: _sources, metadata } = readFixtureJSON(fixturePath); const sources = _sources.map((s) => ({ html: { ...s, + publicKey, clientTime: parseISO(s.clientTime ?? new Date().toISOString()), savingTime: subMinutes(new Date(), 2), }, jsdom: new JSDOM(sanitizeHTML(s.html)).window.document, - supporter: undefined, + supporter: { + publicKey, + }, findings: {}, })); @@ -59,7 +64,7 @@ describe('Leaves parser', () => { log: appTest.logger, sourceSchema: appTest.config.get('schema').leaves, metadataSchema: appTest.config.get('schema').ads, - parsers: { nature: processLeaf }, + parsers: leafParsers, codecs: { contribution: LeafSource, metadata: Ad }, db, getEntryId: (e) => e.html.id, @@ -81,6 +86,7 @@ describe('Leaves parser', () => { const { _id: received_Id, id: receivedId, + publicKey: receivedPublicKey, // sections: sectionsR, // clientTime: clientTimeR, savingTime: savingTimeR, @@ -100,6 +106,7 @@ describe('Leaves parser', () => { clientTime: clientTimeExp, savingTime: savingTimeExp, type: typeExp, + publicKey: expectedPublicKey, // login: loginExp, // blang: blangExp, ...expectedM From 2aa263a0942ab0d9e19d5828140e9e5fe8e03be4 Mon Sep 17 00:00:00 2001 From: ascariandrea Date: Tue, 20 Sep 2022 12:13:10 +0200 Subject: [PATCH 2/2] fix(shared): defined puppeteer hook for type --- packages/shared/src/models/Step.ts | 14 +++++ .../providers/puppeteer/puppeteer.provider.ts | 5 ++ .../src/providers/puppeteer/steps/type.ts | 54 +++++++++++++++++++ platforms/guardoni/src/guardoni/experiment.ts | 25 +++++++++ 4 files changed, 98 insertions(+) create mode 100644 packages/shared/src/providers/puppeteer/steps/type.ts diff --git a/packages/shared/src/models/Step.ts b/packages/shared/src/models/Step.ts index f15a274ce..a2ccbd5c5 100644 --- a/packages/shared/src/models/Step.ts +++ b/packages/shared/src/models/Step.ts @@ -43,6 +43,19 @@ export const ClickStep = t.strict( export type ClickStep = t.TypeOf; +export const TypeType = t.literal('type'); +export const TypeStep = t.strict( + { + type: TypeType, + selector: t.string, + text: t.string, + delay: t.union([t.number, t.undefined]), + }, + 'TypeStep' +); + +export type TypeStep = t.TypeOf; + export const CustomStepType = t.literal('custom'); export type CustomStepType = t.TypeOf; @@ -81,6 +94,7 @@ export const Step = t.union( CustomStep, KeypressStep, ClickStep, + TypeStep, // since `openURL` step is the default and the `type` can be `undefined` // the `OpenURLStep` codec needs to be the last value of the union OpenURLStep, diff --git a/packages/shared/src/providers/puppeteer/puppeteer.provider.ts b/packages/shared/src/providers/puppeteer/puppeteer.provider.ts index 9b4067535..747a5bf85 100644 --- a/packages/shared/src/providers/puppeteer/puppeteer.provider.ts +++ b/packages/shared/src/providers/puppeteer/puppeteer.provider.ts @@ -10,6 +10,7 @@ import { ScrollStepType, KeyPressType, ClickType, + TypeType, } from '../../models/Step'; // import { throwTE } from '../../utils/task.utils'; import { StepHooks } from './StepHooks'; @@ -18,6 +19,7 @@ import { GetScrollFor } from './steps/scroll'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import { GetKeypressStep } from './steps/keyPress'; import { GetClickStep } from './steps/click'; +import { GetTypeStep } from './steps/type'; export type LaunchOptions = puppeteer.LaunchOptions & puppeteer.BrowserLaunchArgumentOptions & @@ -64,6 +66,9 @@ const operateTab = case ClickType.value: { return GetClickStep(ctx)(page, h); } + case TypeType.value: { + return GetTypeStep(ctx)(page, h); + } case CustomStepType.value: return TE.tryCatch(() => { ctx.logger.debug('Custom handler %s', h.handler); diff --git a/packages/shared/src/providers/puppeteer/steps/type.ts b/packages/shared/src/providers/puppeteer/steps/type.ts new file mode 100644 index 000000000..de6c35038 --- /dev/null +++ b/packages/shared/src/providers/puppeteer/steps/type.ts @@ -0,0 +1,54 @@ +import * as E from 'fp-ts/lib/Either'; +import * as TE from 'fp-ts/lib/TaskEither'; +import * as puppeteer from 'puppeteer-core'; +import { AppError, toAppError } from '../../../errors/AppError'; +import { TypeStep } from '../../../models/Step'; +import { sleep } from '../../../utils/promise.utils'; +import { StepContext } from './types'; + +/** + * Click command REgExp + * + * click('video') + */ +export const TYPE_COMMAND_REGEXP = + // eslint-disable-next-line no-useless-escape + /type\(([#|\.]?[\w|:|\s|\.|\-]+);\"([\w|\s|\d]+)\"\)/gm; + +export const parseTypeCommand = ( + cmd: string +): E.Either => { + const match = TYPE_COMMAND_REGEXP.exec(cmd); + // console.log(match); + if (match?.[1] && match[2]) { + const selector: any = match[1]; + const text = match[2]; + const delay = parseInt(match[3], 10); + return E.right({ selector, text, delay }); + } + return E.left( + new AppError('TypeStepError', `Cannot parse command: ${cmd}`, []) + ); +}; +/** + * Type step + * + * Type into an element by the given selector + * + */ + +export const GetTypeStep = + (ctx: StepContext) => + ( + page: puppeteer.Page, + { selector, text, delay: _delay }: TypeStep + ): TE.TaskEither => { + const delay = _delay ?? 0; + + return TE.tryCatch(async () => { + ctx.logger.debug('Type %s with delay %d', selector, delay); + await sleep(delay); + const el = await page.waitForSelector(selector, { timeout: 0 }); + await el?.type(text); + }, toAppError); + }; diff --git a/platforms/guardoni/src/guardoni/experiment.ts b/platforms/guardoni/src/guardoni/experiment.ts index d94acfc1f..63b20f677 100644 --- a/platforms/guardoni/src/guardoni/experiment.ts +++ b/platforms/guardoni/src/guardoni/experiment.ts @@ -10,6 +10,7 @@ import { OpenURLStepType, ScrollStepType, Step, + TypeType, } from '@shared/models/Step'; import * as E from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/function'; @@ -17,6 +18,7 @@ import _ from 'lodash'; // import * as NEA from 'fp-ts/lib/NonEmptyArray'; import { parseClickCommand } from '@shared/providers/puppeteer/steps/click'; import { parseKeypressCommand } from '@shared/providers/puppeteer/steps/keyPress'; +import { parseTypeCommand } from '@shared/providers/puppeteer/steps/type'; import { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'; import * as TE from 'fp-ts/lib/TaskEither'; import * as fs from 'fs'; @@ -145,6 +147,29 @@ export const readCSVAndParse = ) ); } + + if (c.startsWith('type')) { + pipe( + parseTypeCommand(c), + E.fold( + (e) => { + logger.warn(e.name, e.message); + }, + (opts) => { + logger.debug( + 'Type command %s parsed %O', + c, + opts + ); + const typeStep = { + type: TypeType.value, + ...opts, + }; + acc.push(typeStep); + } + ) + ); + } return acc; }, []