testeranto
Version:
the AI powered BDD test framework for typescript projects
396 lines (346 loc) • 11 kB
text/typescript
/* eslint-disable no-async-promise-executor */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import fs from "fs";
import path from "path";
import {
Browser,
Page,
ScreenRecorder,
ScreenshotOptions,
} from "puppeteer-core";
import { PassThrough } from "stream";
import { ITLog } from "../../lib/index.js";
import { IBuiltConfig } from "../../Types.js";
import { IMode } from "../types.js";
const fileStreams3: fs.WriteStream[] = [];
type IFPaths = string[];
const fPaths: IFPaths = [];
const files: Record<string, Set<string>> = {};
const recorders: Record<string, ScreenRecorder> = {};
const screenshots: Record<string, Promise<Uint8Array>[]> = {};
export abstract class PM_0 {
mode: IMode;
browser: Browser;
configs: IBuiltConfig;
projectName: string;
constructor(configs: IBuiltConfig, projectName: string, mode: IMode) {
console.log("mark6", configs);
console.log("mark7", this.configs);
this.configs = configs;
console.log("mark8", this.configs);
this.mode = mode;
this.projectName = projectName;
}
mapping(): [string, (...a) => any][] {
return [
["$", this.$],
["click", this.click],
["closePage", this.closePage],
["createWriteStream", this.createWriteStream],
["customclose", this.customclose],
["customScreenShot", this.customScreenShot.bind(this)],
["end", this.end],
["existsSync", this.existsSync],
["focusOn", this.focusOn],
["getAttribute", this.getAttribute],
["getInnerHtml", this.getInnerHtml],
// ["setValue", this.setValue],
["goto", this.goto.bind(this)],
["isDisabled", this.isDisabled],
// ["launchSideCar", this.launchSideCar.bind(this)],
["mkdirSync", this.mkdirSync],
["newPage", this.newPage],
["page", this.page],
["pages", this.pages],
["screencast", this.screencast],
["screencastStop", this.screencastStop],
// ["stopSideCar", this.stopSideCar.bind(this)],
["typeInto", this.typeInto],
["waitForSelector", this.waitForSelector],
["write", this.write],
["writeFileSync", this.writeFileSync],
];
}
customclose() {
throw new Error("customclose not implemented.");
}
waitForSelector(p: string, s: string): any {
return new Promise((res) => {
this.doInPage(p, async (page) => {
const x = page.$(s);
const y = await x;
res(y !== null);
});
});
}
closePage(p): any {
return new Promise((res) => {
this.doInPage(p, async (page) => {
page.close();
res({});
});
});
}
async newPage(): Promise<string> {
return (await this.browser.newPage()).mainFrame()._id;
}
goto(p, url: string): any {
return new Promise((res) => {
this.doInPage(p, async (page) => {
await page?.goto(url);
res({});
});
});
}
$(selector: string, p: string): Promise<any> {
return new Promise((res) => {
this.doInPage(p, async (page) => {
const x = await page.$(selector);
const y = await x?.jsonValue();
res(y);
});
});
}
async pages(): Promise<string[]> {
return (await this.browser.pages()).map((p) => {
return (p.mainFrame() as any)._id;
});
}
async screencast(ssOpts: ScreenshotOptions, testName: string, page: Page) {
const p = ssOpts.path as string;
const dir = path.dirname(p);
fs.mkdirSync(dir, {
recursive: true,
});
if (!files[testName]) {
files[testName] = new Set();
}
files[testName].add(ssOpts.path as string);
const sPromise = page.screenshot({
...ssOpts,
path: p,
});
if (!screenshots[testName]) {
screenshots[testName] = [];
}
screenshots[testName].push(sPromise);
await sPromise;
return sPromise;
}
async customScreenShot(
ssOpts: ScreenshotOptions,
testName: string,
pageUid: any
) {
const p = ssOpts.path as string;
const dir = path.dirname(p);
fs.mkdirSync(dir, {
recursive: true,
});
if (!files[testName]) {
files[testName] = new Set();
}
files[testName].add(ssOpts.path as string);
const page = (await this.browser.pages()).find(
(p) => p.mainFrame()._id === pageUid
) as Page;
const sPromise = page.screenshot({
...ssOpts,
path: p,
});
if (!screenshots[testName]) {
screenshots[testName] = [];
}
screenshots[testName].push(sPromise);
await sPromise;
return sPromise;
}
async end(uid: number): Promise<boolean> {
await fileStreams3[uid].end();
return true;
}
existsSync(destFolder: string): boolean {
return fs.existsSync(destFolder);
}
async mkdirSync(fp: string) {
if (!fs.existsSync(fp)) {
return fs.mkdirSync(fp, {
recursive: true,
});
}
return false;
}
async writeFileSync(...x): Promise<boolean> {
const filepath = x[0];
const contents = x[1];
const testName = x[2];
return new Promise<boolean>(async (res) => {
fs.mkdirSync(path.dirname(filepath), {
recursive: true,
});
if (!files[testName]) {
files[testName] = new Set();
}
files[testName].add(filepath);
await fs.writeFileSync(filepath, contents);
res(true);
});
}
async createWriteStream(filepath: string, testName: string): Promise<number> {
const folder = filepath.split("/").slice(0, -1).join("/");
return new Promise<number>((res) => {
if (!fs.existsSync(folder)) {
return fs.mkdirSync(folder, {
recursive: true,
});
}
const f = fs.createWriteStream(filepath);
fileStreams3.push(f);
if (!files[testName]) {
files[testName] = new Set();
}
files[testName].add(filepath);
res(fileStreams3.length - 1);
});
}
testArtiFactoryfileWriter(tLog: ITLog, callback: (Promise) => void) {
return (fPath, value: string | Buffer | PassThrough) => {
callback(
new Promise<void>((res, rej) => {
tLog("testArtiFactory =>", fPath);
const cleanPath = path.resolve(fPath);
fPaths.push(cleanPath.replace(process.cwd(), ``));
const targetDir = cleanPath.split("/").slice(0, -1).join("/");
fs.mkdir(targetDir, { recursive: true }, async (error) => {
if (error) {
// TODO
}
fs.writeFileSync(
path.resolve(
targetDir.split("/").slice(0, -1).join("/"),
"manifest"
),
fPaths.join(`\n`),
{
encoding: "utf-8",
}
);
if (Buffer.isBuffer(value)) {
fs.writeFileSync(fPath, value, "binary");
res();
} else if (`string` === typeof value) {
fs.writeFileSync(fPath, value.toString(), {
encoding: "utf-8",
});
res();
} else {
const pipeStream: PassThrough = value;
const myFile = fs.createWriteStream(fPath);
pipeStream.pipe(myFile);
pipeStream.on("close", () => {
myFile.close();
res();
});
}
});
})
);
};
}
async write(uid: number, contents: string): Promise<boolean> {
return new Promise<boolean>((res) => {
const x = fileStreams3[uid].write(contents);
res(x);
});
}
page(p): string | undefined {
return p;
}
click(selector: string, page: Page) {
return page.click(selector);
}
async focusOn(selector: string, p: string) {
this.doInPage(p, (page) => {
return page.focus(selector);
});
}
async typeInto(value: string, p: string) {
this.doInPage(p, (page) => {
return page.keyboard.type(value);
});
}
getAttribute(selector: string, attribute: string, p: string) {
this.doInPage(p, (page) => {
return page.$eval(selector, (input) => input.getAttribute(attribute));
});
}
async getInnerHtml(selector: string, p: string) {
return new Promise((res, rej) => {
this.doInPage(p, async (page) => {
const e = await page.$(selector);
if (!e) {
rej();
} else {
const text = await (await e.getProperty("textContent")).jsonValue();
res(text);
}
});
});
}
isDisabled(selector: string, p: string) {
this.doInPage(p, async (page) => {
return await page.$eval(selector, (input: HTMLButtonElement) => {
return input.disabled;
});
});
}
screencastStop(s: string) {
return recorders[s].stop();
}
async doInPage(p: string, cb: (p: Page) => unknown) {
(await this.browser.pages()).forEach((page: Page) => {
const frame = page.mainFrame() as { _id?: string };
if (frame._id === p) {
return cb(page);
}
});
}
}
// keep this forever. do not delete
// mapping(): [string, (...a) => any][] {
// return [
// ["$", (...args) => this.$(...args)],
// ["click", (...args) => this.click(...args)],
// ["closePage", (...args) => this.closePage(...args)],
// ["createWriteStream", (...args) => this.createWriteStream(...args)],
// ["customclose", (...args) => this.customclose(...args)],
// ["customScreenShot", (...args) => this.customScreenShot(...args)],
// ["end", (...args) => this.end(...args)],
// ["existsSync", (...args) => this.existsSync(...args)],
// ["focusOn", (...args) => this.focusOn(...args)],
// ["getAttribute", (...args) => this.getAttribute(...args)],
// ["getInnerHtml", (...args) => this.getInnerHtml(...args)],
// // ["setValue", (...args) => this.setValue(...args)],
// ["goto", (...args) => this.goto(...args)],
// ["isDisabled", (...args) => this.isDisabled(...args)],
// // ["launchSideCar", (...args) => this.launchSideCar(...args)],
// ["mkdirSync", (...args) => this.mkdirSync(...args)],
// ["newPage", (...args) => this.newPage(...args)],
// ["page", (...args) => this.page(...args)],
// ["pages", (...args) => this.pages(...args)],
// ["screencast", (...args) => this.screencast(...args)],
// ["screencastStop", (...args) => this.screencastStop(...args)],
// // ["stopSideCar", (...args) => this.stopSideCar(...args)],
// ["typeInto", (...args) => this.typeInto(...args)],
// ["waitForSelector", (...args) => this.waitForSelector(...args)],
// ["write", (...args) => this.write(...args)],
// ["writeFileSync", (...args) => this.writeFileSync(...args)],
// ];
// }
// abstract launchSideCar(n: number, testName: string, projectName: string);
// setValue(value: string, p: string) {
// this.doInPage(p, (page) => {
// return page.keyboard.type(value);
// });
// }