chore: use fs.promises API instead of promisify (#6871)
This commit is contained in:
parent
d16afef75a
commit
064150f8dd
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import * as folio from 'folio';
|
||||
import type { LaunchOptions, BrowserContextOptions, Page } from '../../types/types';
|
||||
import type { PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions } from '../../types/test';
|
||||
|
|
@ -139,7 +138,7 @@ export const test = folio.test.extend<PlaywrightTestArgs & PlaywrightTestOptions
|
|||
if (!video)
|
||||
return;
|
||||
const videoPath = await video.path();
|
||||
await util.promisify(fs.unlink)(videoPath).catch(e => {});
|
||||
await fs.promises.unlink(videoPath).catch(e => {});
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { isString } from '../utils/utils';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Events } from './events';
|
||||
|
|
@ -190,7 +189,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel, c
|
|||
const { binary } = await channel.screenshot();
|
||||
const buffer = Buffer.from(binary, 'base64');
|
||||
if (options.path)
|
||||
await util.promisify(fs.writeFile)(options.path, buffer);
|
||||
await fs.promises.writeFile(options.path, buffer);
|
||||
return buffer;
|
||||
});
|
||||
}
|
||||
|
|
@ -274,7 +273,7 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel, c
|
|||
|
||||
async function loadFile(file: string | Buffer): Promise<string> {
|
||||
if (isString(file))
|
||||
return (await util.promisify(fs.readFile)(file)).toString('base64');
|
||||
return fs.promises.readFile(file, { encoding: 'base64' }).toString();
|
||||
return file.toString('base64');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
import { Page, BindingCall } from './page';
|
||||
import * as network from './network';
|
||||
import * as channels from '../protocol/channels';
|
||||
import * as util from 'util';
|
||||
import fs from 'fs';
|
||||
import { ChannelOwner } from './channelOwner';
|
||||
import { deprecate, evaluationScript, urlMatches } from './clientHelper';
|
||||
|
|
@ -35,9 +34,6 @@ import * as structs from '../../types/structs';
|
|||
import { CDPSession } from './cdpSession';
|
||||
import { Tracing } from './tracing';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
|
||||
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel, channels.BrowserContextInitializer> implements api.BrowserContext {
|
||||
_pages = new Set<Page>();
|
||||
private _routes: { url: URLMatch, handler: network.RouteHandler }[] = [];
|
||||
|
|
@ -289,7 +285,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
|
|||
const state = await channel.storageState();
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fsWriteFileAsync(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
await fs.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
|
||||
}
|
||||
return state;
|
||||
});
|
||||
|
|
@ -353,7 +349,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions
|
|||
viewport: options.viewport === null ? undefined : options.viewport,
|
||||
noDefaultViewport: options.viewport === null,
|
||||
extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined,
|
||||
storageState: typeof options.storageState === 'string' ? JSON.parse(await fsReadFileAsync(options.storageState, 'utf8')) : options.storageState,
|
||||
storageState: typeof options.storageState === 'string' ? JSON.parse(await fs.promises.readFile(options.storageState, 'utf8')) : options.storageState,
|
||||
};
|
||||
if (!contextParams.recordVideo && options.videosPath) {
|
||||
contextParams.recordVideo = {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import * as types from './types';
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { isString, isRegExp } from '../utils/utils';
|
||||
|
||||
const deprecatedHits = new Set();
|
||||
|
|
@ -50,7 +49,7 @@ export async function evaluationScript(fun: Function | string | { path?: string,
|
|||
if (fun.content !== undefined)
|
||||
return fun.content;
|
||||
if (fun.path !== undefined) {
|
||||
let source = await util.promisify(fs.readFile)(fun.path, 'utf8');
|
||||
let source = await fs.promises.readFile(fun.path, 'utf8');
|
||||
if (addSourceUrl)
|
||||
source += '//# sourceURL=' + fun.path.replace(/\n/g, '');
|
||||
return source;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,10 @@ import { SelectOption, FilePayload, Rect, SelectOptionOptions } from './types';
|
|||
import fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import path from 'path';
|
||||
import * as util from 'util';
|
||||
import { assert, isString, mkdirIfNeeded } from '../utils/utils';
|
||||
import * as api from '../../types/types';
|
||||
import * as structs from '../../types/structs';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements api.ElementHandle {
|
||||
readonly _elementChannel: channels.ElementHandleChannel;
|
||||
|
||||
|
|
@ -230,7 +227,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
|
|||
const buffer = Buffer.from(result.binary, 'base64');
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fsWriteFileAsync(options.path, buffer);
|
||||
await fs.promises.writeFile(options.path, buffer);
|
||||
}
|
||||
return buffer;
|
||||
});
|
||||
|
|
@ -302,7 +299,7 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
|
|||
if (typeof item === 'string') {
|
||||
return {
|
||||
name: path.basename(item),
|
||||
buffer: (await util.promisify(fs.readFile)(item)).toString('base64')
|
||||
buffer: (await fs.promises.readFile(item)).toString('base64')
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import { ElementHandle, convertSelectOptionValues, convertInputFiles } from './e
|
|||
import { assertMaxArguments, JSHandle, serializeArgument, parseResult } from './jsHandle';
|
||||
import fs from 'fs';
|
||||
import * as network from './network';
|
||||
import * as util from 'util';
|
||||
import { Page } from './page';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Waiter } from './waiter';
|
||||
|
|
@ -32,8 +31,6 @@ import { urlMatches } from './clientHelper';
|
|||
import * as api from '../../types/types';
|
||||
import * as structs from '../../types/structs';
|
||||
|
||||
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
|
||||
export type WaitForNavigationOptions = {
|
||||
timeout?: number,
|
||||
waitUntil?: LifecycleEvent,
|
||||
|
|
@ -275,7 +272,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
|
|||
return this._wrapApiCall(this._apiName('addScriptTag'), async (channel: channels.FrameChannel) => {
|
||||
const copy = { ...options };
|
||||
if (copy.path) {
|
||||
copy.content = (await fsReadFileAsync(copy.path)).toString();
|
||||
copy.content = (await fs.promises.readFile(copy.path)).toString();
|
||||
copy.content += '//# sourceURL=' + copy.path.replace(/\n/g, '');
|
||||
}
|
||||
return ElementHandle.from((await channel.addScriptTag({ ...copy })).element);
|
||||
|
|
@ -286,7 +283,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
|
|||
return this._wrapApiCall(this._apiName('addStyleTag'), async (channel: channels.FrameChannel) => {
|
||||
const copy = { ...options };
|
||||
if (copy.path) {
|
||||
copy.content = (await fsReadFileAsync(copy.path)).toString();
|
||||
copy.content = (await fs.promises.readFile(copy.path)).toString();
|
||||
copy.content += '/*# sourceURL=' + copy.path.replace(/\n/g, '') + '*/';
|
||||
}
|
||||
return ElementHandle.from((await channel.addStyleTag({ ...copy })).element);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import { Frame } from './frame';
|
|||
import { Headers, WaitForEventOptions } from './types';
|
||||
import fs from 'fs';
|
||||
import * as mime from 'mime';
|
||||
import * as util from 'util';
|
||||
import { isString, headersObjectToArray, headersArrayToObject } from '../utils/utils';
|
||||
import { Events } from './events';
|
||||
import { Page } from './page';
|
||||
|
|
@ -196,7 +195,7 @@ export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteIni
|
|||
let isBase64 = false;
|
||||
let length = 0;
|
||||
if (options.path) {
|
||||
const buffer = await util.promisify(fs.readFile)(options.path);
|
||||
const buffer = await fs.promises.readFile(options.path);
|
||||
body = buffer.toString('base64');
|
||||
isBase64 = true;
|
||||
length = buffer.length;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import * as api from '../../types/types';
|
|||
import * as structs from '../../types/structs';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as util from 'util';
|
||||
import { Size, URLMatch, Headers, LifecycleEvent, WaitForEventOptions, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions } from './types';
|
||||
import { evaluationScript, urlMatches } from './clientHelper';
|
||||
import { isString, isRegExp, isObject, mkdirIfNeeded, headersObjectToArray } from '../utils/utils';
|
||||
|
|
@ -48,9 +47,6 @@ import { isSafeCloseError } from '../utils/errors';
|
|||
import { Video } from './video';
|
||||
import { Artifact } from './artifact';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
const mkdirAsync = util.promisify(fs.mkdir);
|
||||
|
||||
type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> & {
|
||||
width?: string | number,
|
||||
height?: string | number,
|
||||
|
|
@ -481,7 +477,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
const buffer = Buffer.from(result.binary, 'base64');
|
||||
if (options.path) {
|
||||
await mkdirIfNeeded(options.path);
|
||||
await fsWriteFileAsync(options.path, buffer);
|
||||
await fs.promises.writeFile(options.path, buffer);
|
||||
}
|
||||
return buffer;
|
||||
});
|
||||
|
|
@ -666,8 +662,8 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
const result = await channel.pdf(transportOptions);
|
||||
const buffer = Buffer.from(result.pdf, 'base64');
|
||||
if (options.path) {
|
||||
await mkdirAsync(path.dirname(options.path), { recursive: true });
|
||||
await fsWriteFileAsync(options.path, buffer);
|
||||
await fs.promises.mkdir(path.dirname(options.path), { recursive: true });
|
||||
await fs.promises.writeFile(options.path, buffer);
|
||||
}
|
||||
return buffer;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import * as channels from '../protocol/channels';
|
|||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { StreamDispatcher } from './streamDispatcher';
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { mkdirIfNeeded } from '../utils/utils';
|
||||
import { Artifact } from '../server/artifact';
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactIn
|
|||
}
|
||||
try {
|
||||
await mkdirIfNeeded(params.path);
|
||||
await util.promisify(fs.copyFile)(localPath, params.path);
|
||||
await fs.promises.copyFile(localPath, params.path);
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import path from 'path';
|
|||
import ProgressBar from 'progress';
|
||||
import { getProxyForUrl } from 'proxy-from-env';
|
||||
import * as URL from 'url';
|
||||
import * as util from 'util';
|
||||
import { BrowserName, Registry, hostPlatform } from '../utils/registry';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
|
||||
|
|
@ -35,8 +34,6 @@ import { debugLogger } from '../utils/debugLogger';
|
|||
// without types.
|
||||
const ProxyAgent = require('https-proxy-agent');
|
||||
|
||||
const unlinkAsync = util.promisify(fs.unlink.bind(fs));
|
||||
const chmodAsync = util.promisify(fs.chmod.bind(fs));
|
||||
const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
|
||||
export type OnProgressCallback = (downloadedBytes: number, totalBytes: number) => void;
|
||||
|
|
@ -96,14 +93,14 @@ export async function downloadBrowserWithProgressBar(registry: Registry, browser
|
|||
await extract(zipPath, { dir: browserDirectory});
|
||||
const executablePath = registry.executablePath(browserName)!;
|
||||
debugLogger.log('install', `fixing permissions at ${executablePath}`);
|
||||
await chmodAsync(executablePath, 0o755);
|
||||
await fs.promises.chmod(executablePath, 0o755);
|
||||
} catch (e) {
|
||||
debugLogger.log('install', `FAILED installation ${progressBarName} with error: ${e}`);
|
||||
process.exitCode = 1;
|
||||
throw e;
|
||||
} finally {
|
||||
if (await existsAsync(zipPath))
|
||||
await unlinkAsync(zipPath);
|
||||
await fs.promises.unlink(zipPath);
|
||||
}
|
||||
logPolitely(`${progressBarName} downloaded to ${browserDirectory}`);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -16,18 +16,12 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import lockfile from 'proper-lockfile';
|
||||
import {Registry, allBrowserNames, isBrowserDirectory, BrowserName, registryDirectory} from '../utils/registry';
|
||||
import * as browserFetcher from './browserFetcher';
|
||||
import { getAsBooleanFromENV, calculateSha1, removeFolders } from '../utils/utils';
|
||||
|
||||
const fsMkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
const fsReaddirAsync = util.promisify(fs.readdir.bind(fs));
|
||||
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
const fsExistsAsync = (filePath: string) => fsReadFileAsync(filePath).then(() => true).catch(e => false);
|
||||
const fsUnlinkAsync = util.promisify(fs.unlink.bind(fs));
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
const fsExistsAsync = (filePath: string) => fs.promises.readFile(filePath).then(() => true).catch(e => false);
|
||||
|
||||
const PACKAGE_PATH = path.join(__dirname, '..', '..');
|
||||
|
||||
|
|
@ -38,7 +32,7 @@ export async function installBrowsersWithProgressBar(browserNames: BrowserName[]
|
|||
return false;
|
||||
}
|
||||
|
||||
await fsMkdirAsync(registryDirectory, { recursive: true });
|
||||
await fs.promises.mkdir(registryDirectory, { recursive: true });
|
||||
const lockfilePath = path.join(registryDirectory, '__dirlock');
|
||||
const releaseLock = await lockfile.lock(registryDirectory, {
|
||||
retries: {
|
||||
|
|
@ -56,8 +50,8 @@ export async function installBrowsersWithProgressBar(browserNames: BrowserName[]
|
|||
const linksDir = path.join(registryDirectory, '.links');
|
||||
|
||||
try {
|
||||
await fsMkdirAsync(linksDir, { recursive: true });
|
||||
await fsWriteFileAsync(path.join(linksDir, calculateSha1(PACKAGE_PATH)), PACKAGE_PATH);
|
||||
await fs.promises.mkdir(linksDir, { recursive: true });
|
||||
await fs.promises.writeFile(path.join(linksDir, calculateSha1(PACKAGE_PATH)), PACKAGE_PATH);
|
||||
await validateCache(linksDir, browserNames);
|
||||
} finally {
|
||||
await releaseLock();
|
||||
|
|
@ -67,11 +61,11 @@ export async function installBrowsersWithProgressBar(browserNames: BrowserName[]
|
|||
async function validateCache(linksDir: string, browserNames: BrowserName[]) {
|
||||
// 1. Collect used downloads and package descriptors.
|
||||
const usedBrowserPaths: Set<string> = new Set();
|
||||
for (const fileName of await fsReaddirAsync(linksDir)) {
|
||||
for (const fileName of await fs.promises.readdir(linksDir)) {
|
||||
const linkPath = path.join(linksDir, fileName);
|
||||
let linkTarget = '';
|
||||
try {
|
||||
linkTarget = (await fsReadFileAsync(linkPath)).toString();
|
||||
linkTarget = (await fs.promises.readFile(linkPath)).toString();
|
||||
const linkRegistry = new Registry(linkTarget);
|
||||
for (const browserName of allBrowserNames) {
|
||||
if (!linkRegistry.isSupportedBrowser(browserName))
|
||||
|
|
@ -88,13 +82,13 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) {
|
|||
usedBrowserPaths.add(usedBrowserPath);
|
||||
}
|
||||
} catch (e) {
|
||||
await fsUnlinkAsync(linkPath).catch(e => {});
|
||||
await fs.promises.unlink(linkPath).catch(e => {});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Delete all unused browsers.
|
||||
if (!getAsBooleanFromENV('PLAYWRIGHT_SKIP_BROWSER_GC')) {
|
||||
let downloadedBrowsers = (await fsReaddirAsync(registryDirectory)).map(file => path.join(registryDirectory, file));
|
||||
let downloadedBrowsers = (await fs.promises.readdir(registryDirectory)).map(file => path.join(registryDirectory, file));
|
||||
downloadedBrowsers = downloadedBrowsers.filter(file => isBrowserDirectory(file));
|
||||
const directories = new Set<string>(downloadedBrowsers);
|
||||
for (const browserDirectory of usedBrowserPaths)
|
||||
|
|
@ -110,7 +104,7 @@ async function validateCache(linksDir: string, browserNames: BrowserName[]) {
|
|||
await browserFetcher.downloadBrowserWithProgressBar(myRegistry, browserName).catch(e => {
|
||||
throw new Error(`Failed to download ${browserName}, caused by\n${e.stack}`);
|
||||
});
|
||||
await fsWriteFileAsync(markerFilePath(myRegistry.browserDirectory(browserName)), '');
|
||||
await fs.promises.writeFile(markerFilePath(myRegistry.browserDirectory(browserName)), '');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import * as types from '../types';
|
|||
import { EventEmitter } from 'events';
|
||||
import fs from 'fs';
|
||||
import * as stream from 'stream';
|
||||
import * as util from 'util';
|
||||
import * as ws from 'ws';
|
||||
import { createGuid, makeWaitForNextTask } from '../../utils/utils';
|
||||
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
|
||||
|
|
@ -34,8 +33,6 @@ import { AndroidWebView } from '../../protocol/channels';
|
|||
import { CRPage } from '../chromium/crPage';
|
||||
import { SdkObject, internalCallMetadata } from '../instrumentation';
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
|
||||
export interface Backend {
|
||||
devices(): Promise<DeviceBackend[]>;
|
||||
}
|
||||
|
|
@ -174,7 +171,7 @@ export class AndroidDevice extends SdkObject {
|
|||
|
||||
debug('pw:android')('Installing the new driver');
|
||||
for (const file of ['android-driver.apk', 'android-driver-target.apk'])
|
||||
await this.installApk(await readFileAsync(require.resolve(`../../../bin/${file}`)));
|
||||
await this.installApk(await fs.promises.readFile(require.resolve(`../../../bin/${file}`)));
|
||||
|
||||
debug('pw:android')('Starting the new driver');
|
||||
this.shell('am instrument -w com.microsoft.playwright.androiddriver.test/androidx.test.runner.AndroidJUnitRunner').catch(e => debug('pw:android')(e));
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { SdkObject } from './instrumentation';
|
||||
|
||||
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
|
||||
|
|
@ -85,7 +84,7 @@ export class Artifact extends SdkObject {
|
|||
return;
|
||||
this._deleted = true;
|
||||
if (fileName)
|
||||
await util.promisify(fs.unlink)(fileName).catch(e => {});
|
||||
await fs.promises.unlink(fileName).catch(e => {});
|
||||
}
|
||||
|
||||
async deleteOnContextClose(): Promise<void> {
|
||||
|
|
@ -95,7 +94,7 @@ export class Artifact extends SdkObject {
|
|||
return;
|
||||
this._deleted = true;
|
||||
if (!this._unaccessibleErrorMessage)
|
||||
await util.promisify(fs.unlink)(this._localPath).catch(e => {});
|
||||
await fs.promises.unlink(this._localPath).catch(e => {});
|
||||
await this.reportFinished('File deleted upon browser context closure.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import path from 'path';
|
||||
import * as util from 'util';
|
||||
import { BrowserContext, normalizeProxySettings, validateBrowserContextOptions } from './browserContext';
|
||||
import * as registry from '../utils/registry';
|
||||
import { ConnectionTransport, WebSocketTransport } from './transport';
|
||||
|
|
@ -33,8 +32,6 @@ import { helper } from './helper';
|
|||
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||
import { CallMetadata, SdkObject } from './instrumentation';
|
||||
|
||||
const mkdirAsync = util.promisify(fs.mkdir);
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
||||
|
||||
|
|
@ -143,15 +140,15 @@ export abstract class BrowserType extends SdkObject {
|
|||
|
||||
const tempDirectories = [];
|
||||
if (options.downloadsPath)
|
||||
await mkdirAsync(options.downloadsPath, { recursive: true });
|
||||
await fs.promises.mkdir(options.downloadsPath, { recursive: true });
|
||||
if (options.tracesDir)
|
||||
await mkdirAsync(options.tracesDir, { recursive: true });
|
||||
await fs.promises.mkdir(options.tracesDir, { recursive: true });
|
||||
|
||||
const artifactsDir = await mkdtempAsync(ARTIFACTS_FOLDER);
|
||||
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
tempDirectories.push(artifactsDir);
|
||||
|
||||
if (!userDataDir) {
|
||||
userDataDir = await mkdtempAsync(path.join(os.tmpdir(), `playwright_${this._name}dev_profile-`));
|
||||
userDataDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `playwright_${this._name}dev_profile-`));
|
||||
tempDirectories.push(userDataDir);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import { CRBrowser } from './crBrowser';
|
||||
import { Env } from '../processLauncher';
|
||||
import { kBrowserCloseMessageId } from './crConnection';
|
||||
|
|
@ -37,7 +36,6 @@ import { CallMetadata } from '../instrumentation';
|
|||
import { findChromiumChannel } from './findChromiumChannel';
|
||||
import http from 'http';
|
||||
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
||||
|
||||
export class Chromium extends BrowserType {
|
||||
|
|
@ -65,7 +63,7 @@ export class Chromium extends BrowserType {
|
|||
if (options.headers)
|
||||
headersMap = headersArrayToObject(options.headers, false);
|
||||
|
||||
const artifactsDir = await mkdtempAsync(ARTIFACTS_FOLDER);
|
||||
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
|
||||
const chromeTransport = await WebSocketTransport.connect(progress, await urlToWSEndpoint(endpointURL), headersMap);
|
||||
const browserProcess: BrowserProcess = {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { CRSession } from './crConnection';
|
||||
|
||||
const kBindingName = '__pw_devtools__';
|
||||
|
|
@ -44,7 +43,7 @@ export class CRDevTools {
|
|||
if (parsed.method === 'getPreferences') {
|
||||
if (this._prefs === undefined) {
|
||||
try {
|
||||
const json = await util.promisify(fs.readFile)(this._preferencesPath, 'utf8');
|
||||
const json = await fs.promises.readFile(this._preferencesPath, 'utf8');
|
||||
this._prefs = JSON.parse(json);
|
||||
} catch (e) {
|
||||
this._prefs = {};
|
||||
|
|
@ -100,7 +99,7 @@ export class CRDevTools {
|
|||
_save() {
|
||||
// Serialize saves to avoid corruption.
|
||||
this._savePromise = this._savePromise.then(async () => {
|
||||
await util.promisify(fs.writeFile)(this._preferencesPath, JSON.stringify(this._prefs)).catch(e => null);
|
||||
await fs.promises.writeFile(this._preferencesPath, JSON.stringify(this._prefs)).catch(e => null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
import { CRSession } from './crConnection';
|
||||
import { Protocol } from './protocol';
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import * as types from '../types';
|
||||
import { mkdirIfNeeded } from '../../utils/utils';
|
||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||
|
|
@ -43,10 +42,10 @@ export async function releaseObject(client: CRSession, objectId: string) {
|
|||
|
||||
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<Buffer> {
|
||||
let eof = false;
|
||||
let fd: number | undefined;
|
||||
let fd: fs.promises.FileHandle | undefined;
|
||||
if (path) {
|
||||
await mkdirIfNeeded(path);
|
||||
fd = await util.promisify(fs.open)(path, 'w');
|
||||
fd = await fs.promises.open(path, 'w');
|
||||
}
|
||||
const bufs = [];
|
||||
while (!eof) {
|
||||
|
|
@ -54,11 +53,11 @@ export async function readProtocolStream(client: CRSession, handle: string, path
|
|||
eof = response.eof;
|
||||
const buf = Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||
bufs.push(buf);
|
||||
if (path)
|
||||
await util.promisify(fs.write)(fd!, buf);
|
||||
if (fd)
|
||||
await fd.write(buf);
|
||||
}
|
||||
if (path)
|
||||
await util.promisify(fs.close)(fd!);
|
||||
if (fd)
|
||||
await fd.close();
|
||||
await client.send('IO.close', {handle});
|
||||
return Buffer.concat(bufs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import { CRBrowser, CRBrowserContext } from '../chromium/crBrowser';
|
||||
import { CRConnection, CRSession } from '../chromium/crConnection';
|
||||
import { CRExecutionContext } from '../chromium/crExecutionContext';
|
||||
|
|
@ -37,7 +36,6 @@ import { RecentLogsCollector } from '../../utils/debugLogger';
|
|||
import { internalCallMetadata, SdkObject } from '../instrumentation';
|
||||
import * as channels from '../../protocol/channels';
|
||||
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
||||
|
||||
export class ElectronApplication extends SdkObject {
|
||||
|
|
@ -125,7 +123,7 @@ export class Electron extends SdkObject {
|
|||
electronArguments.push('--no-sandbox');
|
||||
}
|
||||
|
||||
const artifactsDir = await mkdtempAsync(ARTIFACTS_FOLDER);
|
||||
const artifactsDir = await fs.promises.mkdtemp(ARTIFACTS_FOLDER);
|
||||
|
||||
const browserLogsCollector = new RecentLogsCollector();
|
||||
const { launchedProcess, gracefullyClose, kill } = await launchProcess({
|
||||
|
|
|
|||
|
|
@ -15,15 +15,12 @@
|
|||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { helper } from '../../helper';
|
||||
import * as network from '../../network';
|
||||
import { Page } from '../../page';
|
||||
import * as har from './har';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
type HarOptions = {
|
||||
path: string;
|
||||
omitContent?: boolean;
|
||||
|
|
@ -233,7 +230,7 @@ export class HarTracer {
|
|||
else
|
||||
pageEntry.pageTimings.onLoad = -1;
|
||||
}
|
||||
await fsWriteFileAsync(this._options.path, JSON.stringify({ log: this._log }, undefined, 2));
|
||||
await fs.promises.writeFile(this._options.path, JSON.stringify({ log: this._log }, undefined, 2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as util from 'util';
|
||||
import { CRPage } from '../../chromium/crPage';
|
||||
import { Page } from '../../page';
|
||||
import { ProgressController } from '../../progress';
|
||||
|
|
@ -26,7 +25,6 @@ import type { CallLog, EventData, Mode, Source } from './recorderTypes';
|
|||
import { BrowserContext } from '../../browserContext';
|
||||
import { isUnderTest } from '../../../utils/utils';
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
|
||||
declare global {
|
||||
|
|
@ -57,7 +55,7 @@ export class RecorderApp extends EventEmitter {
|
|||
}
|
||||
|
||||
private async _init() {
|
||||
const icon = await readFileAsync(require.resolve('../../../web/recorder/app_icon.png'));
|
||||
const icon = await fs.promises.readFile(require.resolve('../../../web/recorder/app_icon.png'));
|
||||
const crPopup = this._page._delegate as CRPage;
|
||||
await crPopup._mainFrameSession._client.send('Browser.setDockTile', {
|
||||
image: icon.toString('base64')
|
||||
|
|
@ -67,7 +65,7 @@ export class RecorderApp extends EventEmitter {
|
|||
if (route.request().url().startsWith('https://playwright/')) {
|
||||
const uri = route.request().url().substring('https://playwright/'.length);
|
||||
const file = require.resolve('../../../web/recorder/' + uri);
|
||||
const buffer = await readFileAsync(file);
|
||||
const buffer = await fs.promises.readFile(file);
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
headers: [
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import { EventEmitter } from 'events';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import { BrowserContext } from '../../browserContext';
|
||||
import { Page } from '../../page';
|
||||
import { FrameSnapshot, ResourceSnapshot } from '../../snapshot/snapshotTypes';
|
||||
|
|
@ -25,8 +24,6 @@ import { Snapshotter, SnapshotterBlob, SnapshotterDelegate } from '../../snapsho
|
|||
import { ElementHandle } from '../../dom';
|
||||
import { TraceEvent } from '../common/traceEvents';
|
||||
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
|
||||
export class TraceSnapshotter extends EventEmitter implements SnapshotterDelegate {
|
||||
private _snapshotter: Snapshotter;
|
||||
private _resourcesDir: string;
|
||||
|
|
@ -64,7 +61,7 @@ export class TraceSnapshotter extends EventEmitter implements SnapshotterDelegat
|
|||
|
||||
onBlob(blob: SnapshotterBlob): void {
|
||||
this._writeArtifactChain = this._writeArtifactChain.then(async () => {
|
||||
await fsWriteFileAsync(path.join(this._resourcesDir, blob.sha1), blob.buffer).catch(() => {});
|
||||
await fs.promises.writeFile(path.join(this._resourcesDir, blob.sha1), blob.buffer).catch(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import yazl from 'yazl';
|
||||
import { calculateSha1, createGuid, mkdirIfNeeded, monotonicTime } from '../../../utils/utils';
|
||||
import { Artifact } from '../../artifact';
|
||||
|
|
@ -28,10 +27,6 @@ import { Page } from '../../page';
|
|||
import * as trace from '../common/traceEvents';
|
||||
import { TraceSnapshotter } from './traceSnapshotter';
|
||||
|
||||
const fsAppendFileAsync = util.promisify(fs.appendFile.bind(fs));
|
||||
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
|
||||
const fsMkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
|
||||
export type TracerOptions = {
|
||||
name?: string;
|
||||
snapshots?: boolean;
|
||||
|
|
@ -80,7 +75,7 @@ export class Tracing implements InstrumentationListener {
|
|||
);
|
||||
|
||||
// context + page must be the first events added, no awaits above this line.
|
||||
await fsMkdirAsync(this._resourcesDir, { recursive: true });
|
||||
await fs.promises.mkdir(this._resourcesDir, { recursive: true });
|
||||
|
||||
this._context.instrumentation.addListener(this);
|
||||
if (options.snapshots)
|
||||
|
|
@ -179,7 +174,7 @@ export class Tracing implements InstrumentationListener {
|
|||
};
|
||||
this._appendTraceEvent(event);
|
||||
this._appendEventChain = this._appendEventChain.then(async () => {
|
||||
await fsWriteFileAsync(path.join(this._resourcesDir!, sha1), params.buffer).catch(() => {});
|
||||
await fs.promises.writeFile(path.join(this._resourcesDir!, sha1), params.buffer).catch(() => {});
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
|
@ -207,7 +202,7 @@ export class Tracing implements InstrumentationListener {
|
|||
|
||||
// Serialize all writes to the trace file.
|
||||
this._appendEventChain = this._appendEventChain.then(async () => {
|
||||
await fsAppendFileAsync(this._traceFile!, JSON.stringify(event) + '\n');
|
||||
await fs.promises.appendFile(this._traceFile!, JSON.stringify(event) + '\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { createPlaywright } from '../../playwright';
|
||||
import * as util from 'util';
|
||||
import { PersistentSnapshotStorage, TraceModel } from './traceModel';
|
||||
import { TraceEvent } from '../common/traceEvents';
|
||||
import { ServerRouteHandler, HttpServer } from '../../../utils/httpServer';
|
||||
|
|
@ -27,8 +26,6 @@ import { isUnderTest } from '../../../utils/utils';
|
|||
import { internalCallMetadata } from '../../instrumentation';
|
||||
import { ProgressController } from '../../progress';
|
||||
|
||||
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
|
||||
export class TraceViewer {
|
||||
private _server: HttpServer;
|
||||
private _browserName: string;
|
||||
|
|
@ -76,7 +73,7 @@ export class TraceViewer {
|
|||
response.statusCode = 200;
|
||||
response.setHeader('Content-Type', 'application/json');
|
||||
(async () => {
|
||||
const traceContent = await fsReadFileAsync(tracePrefix + '.trace', 'utf8');
|
||||
const traceContent = await fs.promises.readFile(tracePrefix + '.trace', 'utf8');
|
||||
const events = traceContent.split('\n').map(line => line.trim()).filter(line => !!line).map(line => JSON.parse(line)) as TraceEvent[];
|
||||
const model = new TraceModel(snapshotStorage);
|
||||
model.appendEvents(events, snapshotStorage);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import path from 'path';
|
||||
import * as os from 'os';
|
||||
import { getUbuntuVersion } from '../utils/ubuntuVersion';
|
||||
|
|
@ -22,10 +21,7 @@ import * as registry from '../utils/registry';
|
|||
import * as utils from '../utils/utils';
|
||||
import { printDepsWindowsExecutable } from '../utils/binaryPaths';
|
||||
|
||||
const accessAsync = util.promisify(fs.access.bind(fs));
|
||||
const checkExecutable = (filePath: string) => accessAsync(filePath, fs.constants.X_OK).then(() => true).catch(e => false);
|
||||
const statAsync = util.promisify(fs.stat.bind(fs));
|
||||
const readdirAsync = util.promisify(fs.readdir.bind(fs));
|
||||
const checkExecutable = (filePath: string) => fs.promises.access(filePath, fs.constants.X_OK).then(() => true).catch(e => false);
|
||||
|
||||
export async function validateHostRequirements(registry: registry.Registry, browserName: registry.BrowserName) {
|
||||
if (utils.getAsBooleanFromENV('PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS')) {
|
||||
|
|
@ -195,8 +191,8 @@ function isSharedLib(basename: string) {
|
|||
}
|
||||
|
||||
async function executablesOrSharedLibraries(directoryPath: string): Promise<string[]> {
|
||||
const allPaths = (await readdirAsync(directoryPath)).map(file => path.resolve(directoryPath, file));
|
||||
const allStats = await Promise.all(allPaths.map(aPath => statAsync(aPath)));
|
||||
const allPaths = (await fs.promises.readdir(directoryPath)).map(file => path.resolve(directoryPath, file));
|
||||
const allStats = await Promise.all(allPaths.map(aPath => fs.promises.stat(aPath)));
|
||||
const filePaths = allPaths.filter((aPath, index) => (allStats[index] as any).isFile());
|
||||
|
||||
const executablersOrLibraries = (await Promise.all(filePaths.map(async filePath => {
|
||||
|
|
|
|||
|
|
@ -17,16 +17,13 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as util from 'util';
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
|
||||
export async function getUbuntuVersion(): Promise<string> {
|
||||
if (os.platform() !== 'linux')
|
||||
return '';
|
||||
let osReleaseText = await readFileAsync('/etc/upstream-release/lsb-release', 'utf8').catch(e => '');
|
||||
let osReleaseText = await fs.promises.readFile('/etc/upstream-release/lsb-release', 'utf8').catch(e => '');
|
||||
if (!osReleaseText)
|
||||
osReleaseText = await readFileAsync('/etc/os-release', 'utf8').catch(e => '');
|
||||
osReleaseText = await fs.promises.readFile('/etc/os-release', 'utf8').catch(e => '');
|
||||
if (!osReleaseText)
|
||||
return '';
|
||||
return getUbuntuVersionInternal(osReleaseText);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,10 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import removeFolder from 'rimraf';
|
||||
import * as util from 'util';
|
||||
import * as crypto from 'crypto';
|
||||
import os from 'os';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
const mkdirAsync = util.promisify(fs.mkdir.bind(fs));
|
||||
|
||||
export function spawnAsync(cmd: string, args: string[], options: any): Promise<{stdout: string, stderr: string, code: number, error?: Error}> {
|
||||
const process = spawn(cmd, args, options);
|
||||
|
||||
|
|
@ -134,7 +131,7 @@ export function getAsBooleanFromENV(name: string): boolean {
|
|||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
await mkdirAsync(path.dirname(filePath), {recursive: true}).catch(() => {});
|
||||
await fs.promises.mkdir(path.dirname(filePath), {recursive: true}).catch(() => {});
|
||||
}
|
||||
|
||||
type HeadersArray = { name: string, value: string }[];
|
||||
|
|
|
|||
|
|
@ -20,12 +20,9 @@ import { removeFolders } from '../../lib/utils/utils';
|
|||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as util from 'util';
|
||||
import { RemoteServer, RemoteServerOptions } from './remoteServer';
|
||||
import { baseTest, CommonWorkerFixtures } from './baseTest';
|
||||
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
|
||||
type PlaywrightWorkerOptions = {
|
||||
tracesDir: LaunchOptions['tracesDir'];
|
||||
executablePath: LaunchOptions['executablePath'];
|
||||
|
|
@ -94,7 +91,7 @@ export const playwrightFixtures: folio.Fixtures<PlaywrightTestOptions & Playwrig
|
|||
// - Firefox removes lock file later, presumably from another watchdog process?
|
||||
// - WebKit has circular symlinks that makes CI go crazy.
|
||||
await run(async () => {
|
||||
const dir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
dirs.push(dir);
|
||||
return dir;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
import { browserTest as it, expect } from './config/browserTest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import util from 'util';
|
||||
import crypto from 'crypto';
|
||||
import { chromiumVersionLessThan } from './config/utils';
|
||||
|
||||
|
|
@ -152,9 +151,9 @@ it.describe('download event', () => {
|
|||
const dir = testInfo.outputPath('downloads');
|
||||
const userPath = path.join(dir, 'download.txt');
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(dir)).length).toBe(1);
|
||||
expect((await fs.promises.readdir(dir)).length).toBe(1);
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(dir)).length).toBe(1);
|
||||
expect((await fs.promises.readdir(dir)).length).toBe(1);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ const WebSocketServer = require('ws').Server;
|
|||
const fulfillSymbol = Symbol('fullfil callback');
|
||||
const rejectSymbol = Symbol('reject callback');
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||
const gzipAsync = util.promisify(zlib.gzip.bind(zlib));
|
||||
|
||||
class TestServer {
|
||||
|
|
@ -50,8 +49,8 @@ class TestServer {
|
|||
*/
|
||||
static async createHTTPS(dirPath, port, loopback) {
|
||||
const server = new TestServer(dirPath, port, loopback, {
|
||||
key: fs.readFileSync(path.join(__dirname, 'key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
|
||||
key: await fs.promises.readFile(path.join(__dirname, 'key.pem')),
|
||||
cert: await fs.promises.readFile(path.join(__dirname, 'cert.pem')),
|
||||
passphrase: 'aaaa',
|
||||
});
|
||||
await new Promise(x => server._server.once('listening', x));
|
||||
|
|
@ -273,7 +272,7 @@ class TestServer {
|
|||
if (this._csp.has(pathName))
|
||||
response.setHeader('Content-Security-Policy', this._csp.get(pathName));
|
||||
|
||||
const {err, data} = await readFileAsync(filePath).then(data => ({data})).catch(err => ({err}));
|
||||
const {err, data} = await fs.promises.readFile(filePath).then(data => ({data})).catch(err => ({err}));
|
||||
// The HTTP transaction might be already terminated after async hop here - do nothing in this case.
|
||||
if (response.writableEnded)
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue