How to import plugins in Cypress 9 with Node 16? - node.js

I'm struggling trying to import external libraries to the plugin file.
If I do
const clipboardy = require('clipboardy')
it says "Error [ERR_REQUIRE_ESM]: require() of ES Module /[...]/e2e/node_modules/clipboardy/index.js from /[...]/e2e/cypress/plugins/index.js not supported.". I tried also with
import clipboardy from 'clipboardy'
but this is still not working. I really don't understand how to solve.Can you please help me? I'm on node 16.10 with cypress 9.0.0
Thanks in advance
EDIT: the plugin file
const clipboardy = require("clipboardy");
const csv = require("node-xlsx").default;
const fs = require("fs");
const { lighthouse, pa11y, prepareAudit } = require("cypress-audit");
module.exports = (on, config) => {
on("task", {
parseXlsx({ filePath }) {
return new Promise((resolve, reject) => {
try {
const jsonData = csv.parse(fs.readFileSync(filePath));
resolve(jsonData);
} catch (e) {
reject(e);
}
});
},
getClipboard() {
return clipboardy.readSync();
},
lighthouse: lighthouse(lighthouseReport => {
const categories = lighthouseReport.lhr.categories;
const audits = lighthouseReport.lhr.audits;
const formattedAudit = Object.keys(audits).reduce(
(metrics, curr) => ({
...metrics,
[curr]: audits[curr].numericValue
}),
{}
);
const formattedCategories = Object.keys(categories).reduce(
(metrics, curr) => ({
...metrics,
[curr]: categories[curr].score * 100
}),
{}
);
const results = {
url: lighthouseReport.lhr.requestedUrl,
...formattedCategories
};
console.log("Lighthouse results: [");
console.log(results);
console.log("]");
// fs.writeJSONSync("../results/audit.json", results);
// fs.writeFileSync(
// "audit.json",
// Buffer.from(JSON.stringify(results), "utf8")
// );
}),
pa11y: pa11y(pa11yReport => {
console.log("pa11y results: [");
console.log(pa11yReport);
console.log("]");
})
});
on("before:browser:launch", (browser, launchOptions) => {
prepareAudit(launchOptions);
if (browser.name === "chromium") {
launchOptions.args.push(
"--disable-features=CrossSiteDocumentBlockingIfIsolating,CrossSiteDocumentBlockingAlways,IsolateOrigins,site-per-process"
);
launchOptions.args.push(
"--load-extension=cypress/extensions/Ignore-X-Frame-headers_v1.1"
);
launchOptions.args.push("--disable-dev-shm-usage");
return launchOptions;
}
return launchOptions;
});
return config;
};

The problem was related ti clipboardy, which in version 3 became a ES Only modulo, not compatibile with Cypress. Downgrading to v2.3 solved the issue

Related

How to run a fetch inside a Vite Plugin

Am creating a vite plugin which will enable me to generate a sitemap for my website but am not finding how to make a fetch request using one of their many hooks. Am using simplecrawler module to run the fetch inside the plugin but it doesn't seem to work.
import type { Plugin, ResolvedConfig } from "vite";
import crawler from "simplecrawler";
const crawl = async (port:number): Promise<string[]> => {
return new Promise((resolve)=> {
const links: Set<string> = new Set();
const Crawler = new crawler(`http://localhost:${port}`);
Crawler.on("fetchcomplete", (queueItem, _responseBody, response) => {
if (response.headers["content-type"] === "text/html") {
links.add(queueItem.url);
}
});
Crawler.on("complete", () => {
const paths = [...links].map((link) => {
return new URL(link).pathname;
});
resolve(paths);
});
Crawler.start()
});
};
const plugin = ():Plugin =>{
const moduleId = `virtual:sitemap`;
const resolvedModuleId = `\0${moduleId}`;
let config: ResolvedConfig;
return {
name: "vite-plugin-svelte-sitemap",
resolveId: id => {
if (id === moduleId){
return resolvedModuleId
}
return id
},
configResolved: resolvedConfig => {
config = resolvedConfig;
},
buildStart: ()=>{
return async ()=>{
let port = config.server.port !== undefined ? config.server.port : (config.mode === "development" ? 5173 : 4173);
let urls = await crawl(port);
console.log(urls);
}
}
}
}
export default plugin;
So far, this is what I have been able to come up with

Rxdb sync not update db

There are 3 bases (front, node, remote). Front <=> node, node <=> remote. When the front base is updated, the data goes to the remote base, but the node is not updated. In theory, the node should be updated first, and then the remote base.
Render db
addPouchPlugin(PouchdbAdapterIdb)
addPouchPlugin(PouchHttpPlugin)
addRxPlugin(RxDBReplicationCouchDBPlugin)
addRxPlugin(RxDBMigrationPlugin)
addRxPlugin(RxDBLeaderElectionPlugin)
addRxPlugin(RxDBQueryBuilderPlugin)
addRxPlugin(RxDBAjvValidatePlugin)
addRxPlugin(RxDBUpdatePlugin)
export const createDb = async () => {
console.log('[src/renderer/database/createDb] createDb')
const productsName = collectionName.getCollectionProductsName()
const documentsName = collectionName.getCollectionDocumentsName()
const settingsName = collectionName.getCollectionSettingsName()
const db = await createRxDatabase<Collections>({
name: 'renderer',
// use pouchdb with the indexeddb-adapter as storage engine.
storage: getRxStoragePouch('idb'),
})
await initCommonCollections({ db, documentsName, productsName, settingsName })
syncDbCollections(db, [productsName, documentsName, settingsName])
db.$.subscribe(({ operation, documentId, documentData }) => {
if (documentData.type === SettingsTypes.DEVICE_SETTING) {
console.log(`Change database RENDER event:\n ${operation}, \n documentData:`, documentData)
}
})
return db
}
Render sync
const remoteDbUrl = `http://localhost:3030/db/`
const logPath = '[src/renderer/database/syncDbCollections]'
export const syncDbCollections = (db: RxDatabase<Collections>, collectionNames: (keyof Collections)[]) => {
console.log('syncDbCollections', collectionNames)
collectionNames.forEach(name => {
const rxReplicationState = db.collections[name].syncCouchDB({
remote: `${remoteDbUrl}${name}`,
options: {
live: true,
retry: true,
},
})
rxReplicationState.error$.subscribe(error => {
console.error(logPath, name, 'error', JSON.stringify(error))
})
})
}
Node base
addPouchPlugin(PouchdbAdapterHttp)
addPouchPlugin(LevelDbAdapter)
addRxPlugin(RxDBAjvValidatePlugin)
addRxPlugin(RxDBMigrationPlugin)
addRxPlugin(RxDBServerPlugin)
addRxPlugin(RxDBLeaderElectionPlugin)
addRxPlugin(RxDBQueryBuilderPlugin)
addRxPlugin(RxDBUpdatePlugin)
addRxPlugin(RxDBReplicationCouchDBPlugin)
let db: RxDatabase<Collections>
export const getMainDb = () => {
if (!db) {
throw new Error('No available database.')
}
return db
}
export const getDocumentCollection = (): DocumentsRxCol => {
return db[collectionNames.getCollectionDocumentsName()]
}
export const getSettingsCollection = (): SettingsRxCol => {
return db[collectionNames.getCollectionSettingsName()]
}
export const getProductsCollection = (): ProductsRxCol => {
return db[collectionNames.getCollectionProductsName()]
}
export const initDatabase = async () => {
console.log(logPathAlias, 'initDatabase')
if (db) {
console.warn(logPathAlias, 'db instance already created!')
return db
}
db = await createRxDatabase<Collections>({
name: `${electronApp.getPath('userData')}/db`,
storage: getRxStoragePouch(LevelDown),
})
const productsName = collectionNames.getCollectionProductsName()
const documentsName = collectionNames.getCollectionDocumentsName()
const settingsName = collectionNames.getCollectionSettingsName()
await initCommonCollections({ db, productsName, documentsName, settingsName })
await syncCollections([productsName, documentsName, settingsName])
db.$.subscribe(({ operation, documentId, documentData }) => {
// if (documentData.type === SettingsTypes.DEVICE_SETTING) {
console.log(`Change database NODE event:\n ${operation}, \n documentData:`, documentData)
// }
})
const { app } = await db.server({
startServer: false, // (optional), start express server
// options of the pouchdb express server
cors: false,
pouchdbExpressOptions: {
inMemoryConfig: true, // do not write a config.json
logPath: `${electronApp.getPath('temp')}/rxdb-server.log`, // save logs in tmp folder
},
})
return app
}
const lastRetryTime = {}
const syncCollections = async (collections: CollectionNames[]) => {
collections.map(collectionName => {
const rxReplicationState = db.collections[collectionName].syncCouchDB({
remote: `${CouchDbServerUrl}/${collectionName}`,
options: {
live: true,
retry: true,
// #ts-ignore
// headers: {
// Authorization: `Bearer ${getAccessToken()}`,
// },
},
})
rxReplicationState.error$.subscribe(async error => {
console.error(logPathAlias, collectionName, String(error))
if (error.status === 401 && dayjs().diff(lastRetryTime[collectionName], 'seconds') > 10 && getIsRefreshFresh()) {
lastRetryTime[collectionName] = dayjs()
await rxReplicationState.cancel()
await refreshTokens()
await syncCollections([collectionName])
}
})
})
}
No errors
Moreover, if you save data in a remote database, then they are synchronized with the node
Help me :(

cannot mock a fetch call with `fetch-mock-jest 1.5.1` lib

I'm trying to mock a fetch call using thisfetch-mock-jest but it the code still trys to go to the remote address and eventually fail with error message FetchError: request to https://some.domain.io/app-config.yaml failed, reason: getaddrinfo ENOTFOUND some.domain.io].
Here the the test code
import { AppConfig } from '#backstage/config';
import { loadConfig } from './loader';
import mockFs from 'mock-fs';
import fetchMock from 'fetch-mock-jest';
describe('loadConfig', () => {
beforeEach(() => {
fetchMock.mock({
matcher: '*',
response: `app:
title: Example App
sessionKey: 'abc123'
`
});
});
afterEach(() => {
fetchMock.mockReset();
});
it('load config from remote path', async () => {
const configUrl = 'https://some.domain.io/app-config.yaml';
await expect(
loadConfig({
configRoot: '/root',
configTargets: [{ url: configUrl }],
env: 'production',
remote: {
reloadIntervalSeconds: 30,
},
})
).resolves.toEqual([
{
context: configUrl,
data: {
app: {
title: 'Example App',
sessionKey: 'abc123',
},
},
},
]);
expect(fetchMock).toHaveBeenCalledTimes(1);
});
function defer<T>() {
let resolve: (value: T) => void;
const promise = new Promise<T>(_resolve => {
resolve = _resolve;
});
return { promise, resolve: resolve! };
}
});
loadConfig has the fetch code that I'm trying to mock.
export async function loadConfig(
options: LoadConfigOptions,
): Promise<AppConfig[]> {
const loadRemoteConfigFiles = async () => {
const configs: AppConfig[] = [];
const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => {
const response = await fetch(remoteConfigProp.url);
if (!response.ok) {
throw new Error(
`Could not read config file at ${remoteConfigProp.url}`,
);
}
remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined;
remoteConfigProp.newETag =
response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined;
remoteConfigProp.content = await response.text();
return remoteConfigProp;
};
.......
return configs;
}
let remoteConfigs: AppConfig[] = [];
if (remote) {
try {
remoteConfigs = await loadRemoteConfigFiles();
} catch (error) {
throw new Error(`Failed to read remote configuration file, ${error}`);
}
}
........ do some stuff with config then return
return remoteConfigs;
}
The config is a yaml file, that eventually gets parsed and converted into config object.
Any idea why is it failing to mock the fetch call?
replaced
import fetchMock from 'fetch-mock-jest';
with
const fetchMock = require('fetch-mock').sandbox();
const nodeFetch = require('node-fetch');
nodeFetch.default = fetchMock;
and fetchMock.mockReset(); with fetchMock.restore();

Unit test for customPollingHook which uses apollo useLazyQuery

So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});

how to fix octokit.authenticate() is deprecated

I am starting the ginit module but getting the error like this:
=> octokit.authenticate() is deprecated. Use "auth" constructor
option instead.
How can I fix it?
my code
module.exports = {
getInstance: () => {
return octokit;
},
setGithubCredentials : async () => {
const credentials = await inquirer.askGithubCredentials();
octokit.authenticate(
_.extend(
{
type: 'basic',
},
credentials
)
);
},
}
Maybe you code from this article: https://www.sitepoint.com/javascript-command-line-interface-cli-node-js/
And my solution is below
const Octokit = require("#octokit/rest");
const Configstore = require("configstore");
const pkg = require("../package.json");
const _ = require("lodash");
const CLI = require("clui");
const Spinner = CLI.Spinner;
const chalk = require("chalk");
const inquirer = require("./inquirer");
const conf = new Configstore(pkg.name);
module.exports = {
getInstance: () => {
return global.octokit;
},
getStoredGithubToken: () => {
return conf.get("github.token");
},
setGithubCredentials: async () => {
const credentials = await inquirer.askGithubCredentials();
const result = _.extend(
{
type: "basic"
},
credentials
);
global.octokit = Octokit({
auth: result
});
},
registerNewToken: async () => {
const status = new Spinner("Authenticating you, please wait...");
status.start();
try {
const response = await global.octokit.oauthAuthorizations.createAuthorization({
scopes: ["user", "public_repo", "repo", "repo:status"],
note: "ginits, the command-line tool for initalizing Git repos"
});
const token = response.data.token;
if (token) {
conf.set("github.token", token);
return token;
} else {
throw new Error(
"Missing Token",
"GitHub token was not found in the response"
);
}
} catch (err) {
throw err;
} finally {
status.stop();
}
}
};
Try something like:
const Octokit = require('#octokit/rest');
module.export = {
getInstance({username, password}) {
return Octokit({
auth: {
username,
password,
},
});
}
}
The PR introducing the auth property shows some other examples of specifying credentials.

Resources