UNPKG

@jayree/sfdx-plugin-org

Version:

A Salesforce CLI plugin containing commands to configure State and Country/Territory Picklists and other org settings.

296 lines 13.1 kB
/* * Copyright 2026, jayree * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import playwright from 'playwright-chromium'; import chalk from 'chalk'; import Debug from 'debug'; import { SfProject } from '@salesforce/core'; import { readLaunchOptionsFromProject } from './utils.js'; const debug = Debug('jayree:org:configure'); export async function readTasksFromProject() { const proj = await SfProject.resolve(); const projJson = (await proj.resolveProjectConfig()); const tasks = []; if (projJson.plugins?.['jayree/sfdx-plugin-org']?.tasks) { for (const [key, value] of Object.entries(projJson.plugins?.['jayree/sfdx-plugin-org']?.tasks)) { tasks.push({ title: key, ...value }); } } return tasks; } export class PuppeteerConfigureTasks { currenTask; tasks; nextTaskIndex = -1; browser; context; auth; constructor(auth, tasks) { this.tasks = tasks; this.auth = auth; } // eslint-disable-next-line complexity static async subExec(page, task) { for await (const call of task.evaluate) { if (call.action === 'click') { try { if (typeof call.waitFor === 'string') { if (call.waitFor === 'Navigation') { // Nothing to do } else if (call.waitFor !== '') { const value = await page.evaluate((c) => { const element = document.querySelector(c.waitFor); return Boolean(element && (element.offsetWidth || element.offsetHeight || element.getClientRects().length)); }, call); if (value === true) { debug(`checked already ${call.type.checkbox.checked}`); return false; } } } if (typeof call.waitFor === 'object') { if (typeof call.waitFor.querySelector === 'string') { let value; if (typeof call.waitFor.property !== 'undefined') { value = await page.evaluate((c) => { // eslint-disable-next-line no-underscore-dangle const _value = document.querySelector(c.querySelector); if (_value !== null) { return _value[c.property].trim().includes(c.value); } throw new Error(`property ${c.property} not found`); }, call.waitFor); } if (value === true) { debug('already done'); return false; } } } if (typeof call.type === 'object' && call.type.checkbox) { const state = await page.evaluate((c) => ({ checked: document.querySelector(c.querySelector)?.checked, disabled: document.querySelector(c.querySelector)?.disabled, }), call); debug(state); if (state.checked === call.type.checkbox.checked) { debug(`checked already ${call.type.checkbox.checked}`); return false; } else if (state.disabled) { throw new Error('checkbox disabled'); } else { await page.evaluate((c) => { document.querySelector(c.querySelector)?.click(); }, call); } } if (typeof call.type === 'object' && call.type.list) { await page.waitForSelector('table', { timeout: 300_000, state: 'visible', }); debug({ [call.querySelectorAll]: await page.evaluate((c) => { const elements = document.querySelectorAll(c.querySelectorAll); const ret = []; elements.forEach((element) => { ret.push(element.textContent?.trim()); }); return ret; }, call), }); const found = await page.evaluate((c) => { const elements = document.querySelectorAll(c.querySelectorAll); let ret = ''; elements.forEach((element) => { if (element.textContent?.trim() === c.type.list.selection) { element.click(); ret = element.textContent.trim(); } }); return ret; }, call); if (found !== call.type.list.selection) { debug(`value ${call.type.list.selection} not found`); throw new Error(`value ${call.type.list.selection} not found`); } } if (typeof call.type === 'string' && call.type === 'button') { if (await page.$(call.querySelector)) { await page.evaluate((c) => { document.querySelector(c)?.click(); }, call.querySelector); } else { debug('button not found'); throw new Error('button not found'); } } if (typeof call.type === 'string' && call.type === 'lightningbutton') { const myCanvas = await page.$(call.querySelector); if (myCanvas) { const myCanvasBox = await myCanvas.boundingBox(); if (myCanvasBox) { await page.mouse.click(myCanvasBox.x + myCanvasBox.width / 2, myCanvasBox.y + myCanvasBox.height / 2); } } else { debug('button not found'); throw new Error('button not found'); } } } catch (error) { throw new Error(error.message); } } if (call.action === 'type') { if (typeof call.value === 'string') { const value = await page.evaluate((c) => { const element = document.querySelector(c); return element?.value; }, call.querySelector); if (value === call.value) { debug(`value already ${call.value}`); return false; } else { await page.evaluate((c) => { const element = document.querySelector(c.querySelector); element.value = c.value; }, call); } } } if (typeof call.waitFor === 'string') { if (call.waitFor === 'Navigation') { await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 300_000, }); } else if (call.waitFor !== '') { await page.waitForSelector(call.waitFor, { state: 'visible', timeout: 300_000, }); } } if (Array.isArray(call.waitFor)) { if (call.waitFor[0] === 'not') { /* eslint no-constant-condition: ["error", { "checkLoops": false }] */ while (true) { try { // eslint-disable-next-line no-await-in-loop await page.waitForSelector(call.waitFor[1], { state: 'hidden' }); break; } catch (e) { debug(e.message); // eslint-disable-next-line no-await-in-loop await page.reload({ waitUntil: 'networkidle', }); continue; } } } else { await page.waitForSelector(call.waitFor[1], { state: 'visible', timeout: 300_000, }); } } if (typeof call.waitFor === 'object') { if (typeof call.waitFor.querySelector === 'string') { if (typeof call.waitFor.property !== 'undefined') { await page.waitForFunction((c) => { const value = document.querySelector(c.querySelector); if (value !== null) { return value[c.property]?.trim().includes(c.value); } throw new Error(`property ${c.property} not found`); }, call.waitFor, { timeout: 300_000, }); } } } } return true; } getNext() { this.nextTaskIndex = this.nextTaskIndex + 1; this.currenTask = this.tasks[this.nextTaskIndex]; return this; } async close() { if (this.browser) { await this.browser.close(); } } async open() { if (!this.browser) { this.browser = await playwright['chromium'].launch(await readLaunchOptionsFromProject()); this.context = await this.browser.newContext(); const login = await this.context.newPage(); await login.goto(`${this.auth.instanceUrl}/secur/frontdoor.jsp?sid=${this.auth.accessToken}`, { waitUntil: 'networkidle', timeout: 300_000, }); } } async execute(listrTask) { const task = this.currenTask; if (!this.browser) { await this.open(); } if (!task.tasks) { task.tasks = [{ evaluate: task.evaluate }]; } let retValue = false; for await (const subTask of task.tasks) { const page = await this.context.newPage(); await page.goto(this.auth.instanceUrl + task.url, { waitUntil: 'networkidle', timeout: 300_000, }); if (task.iframe) { await page.waitForSelector('iframe', { timeout: 300_000, state: 'visible' }); await page.goto(page.frames()[task.iframe].url(), { waitUntil: 'networkidle', timeout: 300_000, }); } if ((await PuppeteerConfigureTasks.subExec(page, subTask)) === true) { retValue = true; if (subTask.title) { listrTask.output = `${subTask.title}`; } } else if (subTask.title) { listrTask.output = `${subTask.title} ${chalk.dim('[SKIPPED]')}`; } debug({ retValue }); } debug({ retValue }); return retValue; } } //# sourceMappingURL=configuretasks.js.map