puppeteer#Page TypeScript Examples

The following examples show how to use puppeteer#Page. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: puppet-account.ts    From epicgames-freegames-node with MIT License 6 votes vote down vote up
private async waitForHCaptcha(page: Page): Promise<'captcha' | 'nav'> {
    try {
      const talonHandle = await page.$('iframe#talon_frame_registration_prod');
      if (!talonHandle) throw new Error('Could not find talon_frame_registration_prod');
      const talonFrame = await talonHandle.contentFrame();
      if (!talonFrame) throw new Error('Could not find talonFrame contentFrame');
      this.L.trace('Waiting for hcaptcha iframe');
      await talonFrame.waitForSelector(`#challenge_container_hcaptcha > iframe[src*="hcaptcha"]`, {
        visible: true,
      });
      return 'captcha';
    } catch (err) {
      if (err.message.includes('timeout')) {
        throw err;
      }
      if (err.message.includes('detached')) {
        this.L.trace(err);
      } else {
        this.L.warn(err);
      }
      return 'nav';
    }
  }
Example #2
Source File: cloudflare.ts    From FlareSolverr with MIT License 6 votes vote down vote up
async function findAnySelector(page: Page, selectors: string[]) {
  for (const selector of selectors) {
    const cfChallengeElem = await page.$(selector)
    if (cfChallengeElem) {
      return selector;
    }
  }
  return null;
}
Example #3
Source File: puppet-account.ts    From epicgames-freegames-node with MIT License 6 votes vote down vote up
private async handleCaptcha(page: Page): Promise<void> {
    const action = await this.waitForHCaptcha(page);
    if (action === 'nav') return;
    this.L.debug('Captcha detected');
    const url = await page.openPortal();
    this.L.info({ url }, 'Go to this URL and do something');
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    new LocalNotifier(null as any).sendNotification(url);
    await page.waitForSelector(`input[name='code-input-0']`, {
      timeout: NOTIFICATION_TIMEOUT,
    });
    await page.closePortal();
  }
Example #4
Source File: login.ts    From cli with Apache License 2.0 6 votes vote down vote up
async function getLoginPage(browser: Browser) {
  const page = (await browser.pages()).find(isLoginPage);
  if (page) {
    return page;
  }
  return new Promise<Page>((resolve) => {
    browser.on('targetchanged', async (target: Target) => {
      const page = await target.page();
      if (page && isLoginPage(page)) {
        resolve(page);
      }
    });
  });
}
Example #5
Source File: helpers.ts    From gatsby-plugin-next-seo with MIT License 6 votes vote down vote up
launch = async ({
  path = '/',
  disableJavascript = false,
}: LaunchParams = {}): Promise<ElementHandle> => {
  if (disableJavascript) {
    await page.setJavaScriptEnabled(false);
  }

  await page.goto(
    url(path),
    disableJavascript ? {} : { waitUntil: 'domcontentloaded' },
  );

  if (clientOnly) {
    await page.waitFor(500); // gatsby develop takes a moment to warm up on first load
  }

  return getDocument(page as Page);
}
Example #6
Source File: Pdf.ts    From pdf-generator-service with MIT License 6 votes vote down vote up
private static async generateToc(pdfBuffer: Buffer, options: PdfOptions, page: Page): Promise<Buffer> {
    if (options.tocTemplate) {
      await extractPDFToc(pdfBuffer, options)
      await page.setContent(options.content, { waitUntil: 'networkidle0' })
      const pdfOptions = Pdf.buildPdfArguments(options, false)
      return await page.pdf(pdfOptions)
    }

    return pdfBuffer
  }
Example #7
Source File: browser.test.ts    From web with MIT License 6 votes vote down vote up
function trackErrors(page: Page) {
  const errors: any[] = [];
  page.on('error', error => {
    errors.push(error);
  });
  page.on('console', e => {
    if (e.type() === 'error' || e.type() === 'warning') {
      errors.push(e.text());
    }
  });
  return errors;
}
Example #8
Source File: login.ts    From epicgames-freegames-node with MIT License 6 votes vote down vote up
async refreshLogin(page: Page): Promise<boolean> {
    await page.goto(STORE_CART_EN, {
      waitUntil: 'networkidle0',
    });
    const cdpClient = await page.target().createCDPSession();
    const currentUrlCookies = (await cdpClient.send('Network.getAllCookies')) as {
      cookies: Protocol.Network.Cookie[];
    };
    if (currentUrlCookies.cookies.find((c) => c.name === 'storeTokenExpires')) {
      return true;
    }
    return false;
  }
Example #9
Source File: puppeteerCtx.ts    From mockiavelli with MIT License 6 votes vote down vote up
function makeRequestFactory(page: Page) {
    return (
        method: string,
        url: string,
        headers?: Record<string, string>,
        body?: any,
        options?: { waitForRequestEnd: boolean }
    ): ReturnType<typeof makeRequest> =>
        page.evaluate(makeRequest, {
            url,
            method,
            headers,
            body,
            options,
        });
}
Example #10
Source File: free-games.ts    From epicgames-freegames-node with MIT License 5 votes vote down vote up
private page?: Page;
Example #11
Source File: PuppeteerRunnerFactory.ts    From double-agent with MIT License 5 votes vote down vote up
page: Page;
Example #12
Source File: crawler.service.ts    From nest-puppeteer with MIT License 5 votes vote down vote up
constructor(@InjectPage() private readonly page: Page) {}
Example #13
Source File: Pdf.ts    From pdf-generator-service with MIT License 5 votes vote down vote up
private static async generateContent(options: PdfOptions, page: Page): Promise<Buffer> {
    await page.setContent(options.content, { waitUntil: 'networkidle0' })
    const pdfOptions = Pdf.buildPdfArguments(options, false)
    return await page.pdf(pdfOptions)
  }
Example #14
Source File: solver.ts    From FlareSolverr with MIT License 5 votes vote down vote up
async function gotoPage(params: V1Request, page: Page): Promise<HTTPResponse> {
    let pageTimeout = params.maxTimeout / 3;
    let response: HTTPResponse
    try {
        response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
    } catch (e) {
        // retry
        response = await page.goto(params.url, {waitUntil: 'domcontentloaded', timeout: pageTimeout});
    }

    if (params.method == 'POST') {
        // post hack
        await page.setContent(
            `
<!DOCTYPE html>
<html>
<body>
<script>

  function parseQuery(queryString) {
    var query = {};
    var pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
    for (var i = 0; i < pairs.length; i++) {
        var pair = pairs[i].split('=');
        query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
    }
    return query;
  }

  const form = document.createElement('form');
  form.method = 'POST';
  form.action = '${params.url}';

  const params = parseQuery('${params.postData}');
  for (const key in params) {
    if (params.hasOwnProperty(key)) {
      const hiddenField = document.createElement('input');
      hiddenField.type = 'hidden';
      hiddenField.name = key;
      hiddenField.value = params[key];
      form.appendChild(hiddenField);
    }
  }

  document.body.appendChild(form);
  form.submit();
    
</script>
</body>
</html> 
            `
        );
        await page.waitForTimeout(2000)
        try {
            await page.waitForNavigation({waitUntil: 'domcontentloaded', timeout: 2000})
        } catch (e) {}

    }
    return response
}
Example #15
Source File: routes.ts    From CloudProxy with MIT License 5 votes vote down vote up
async function interceptResponse(page: Page, callback: (payload: ChallengeResolutionT) => any) {
  const client = await page.target().createCDPSession();
  await client.send('Fetch.enable', {
    patterns: [
      {
        urlPattern: '*',
        resourceType: 'Document',
        requestStage: 'Response',
      },
    ],
  });

  client.on('Fetch.requestPaused', async (e) => {
    log.debug('Fetch.requestPaused. Checking if the response has valid cookies')
    let headers = e.responseHeaders || []

    let cookies = await page.cookies();
    log.debug(cookies)

    if (cookies.filter((c: Cookie) => c.name === 'cf_clearance').length > 0) {
      log.debug('Aborting request and return cookies. valid cookies found')
      await client.send('Fetch.failRequest', {requestId: e.requestId, errorReason: 'Aborted'})

      let status = 'ok'
      let message = ''
      const payload: ChallengeResolutionT = {
        status,
        message,
        result: {
          url: page.url(),
          status: e.status,
          headers: headers.reduce((a: any, x: { name: any; value: any }) => ({ ...a, [x.name]: x.value }), {}),
          response: null,
          cookies: cookies,
          userAgent: ''
        }
      }

      callback(payload);
    } else {
      log.debug('Continuing request. no valid cookies found')
      await client.send('Fetch.continueRequest', {requestId: e.requestId})
    }
  });
}
Example #16
Source File: login.ts    From cli with Apache License 2.0 5 votes vote down vote up
function isLoginPage(page: Page) {
  const loginUrl = new URL('/login', process.env.PLATFORM_HOST);
  return page.url() === loginUrl.href;
}
Example #17
Source File: index.ts    From ppeforfree with GNU General Public License v3.0 5 votes vote down vote up
private page: Page;
Example #18
Source File: login.ts    From epicgames-freegames-node with MIT License 5 votes vote down vote up
private async waitForRedirectNav(page: Page, timeout = NOTIFICATION_TIMEOUT): Promise<'nav'> {
    return page
      .waitForResponse((res) => res.url() === STORE_HOMEPAGE_EN, { timeout })
      .then(() => 'nav');
  }
Example #19
Source File: index.ts    From ppeforfree with GNU General Public License v3.0 5 votes vote down vote up
constructor(page: Page) {
    this.page = page;
  }
Example #20
Source File: routes.ts    From CloudProxy with MIT License 5 votes vote down vote up
async function setupPage(ctx: RequestContext, params: BaseRequestAPICall, browser: Browser): Promise<Page> {
  const page = await browser.newPage()

  // merge session defaults with params
  const { method, postData, userAgent, headers, cookies, proxy } = params

  let overrideResolvers: OverrideResolvers = {}

  if (method !== 'GET') {
    log.debug(`Setting method to ${method}`)
    overrideResolvers.method = request => method
  }

  if (postData) {
    log.debug(`Setting body data to ${postData}`)
    overrideResolvers.postData = request => postData
  }

  if (userAgent) {
    log.debug(`Using custom UA: ${userAgent}`)
    await page.setUserAgent(userAgent)
  }

  if (headers) {
    log.debug(`Adding custom headers: ${JSON.stringify(headers, null, 2)}`,)
    overrideResolvers.headers = request => Object.assign(request.headers(), headers)
  }

  if (cookies) {
    log.debug(`Setting custom cookies: ${JSON.stringify(cookies, null, 2)}`,)
    await page.setCookie(...cookies)
  }

  // if any keys have been set on the object
  if (Object.keys(overrideResolvers).length > 0) {
    log.debug(overrideResolvers)
    let callbackRunOnce = false
    const callback = (request: Request) => {

      if (callbackRunOnce || !request.isNavigationRequest()) {
        request.continue()
        return
      }

      callbackRunOnce = true
      const overrides: Overrides = {}

      Object.keys(overrideResolvers).forEach((key: OverridesProps) => {
        // @ts-ignore
        overrides[key] = overrideResolvers[key](request)
      });

      log.debug(overrides)

      request.continue(overrides)
    }

    await page.setRequestInterception(true)
    page.on('request', callback)
  }

  return page
}
Example #21
Source File: angular.specs.ts    From cli with Apache License 2.0 4 votes vote down vote up
// TODO CDX-804: Enable the tests back
describe('ui:create:angular', () => {
  let browser: Browser;
  const processManagers: ProcessManager[] = [];
  let page: Page;
  const oldEnv = process.env;
  const parentDir = 'angular';
  const projectName = `${process.env.TEST_RUN_ID}-${parentDir}-project`;
  const projectPath = join(getUIProjectPath(), parentDir, projectName);
  let clientPort: number;
  let serverPort: number;
  const angularJsonPath = join(projectPath, 'angular.json');

  const searchPageEndpoint = () => `http://localhost:${clientPort}`;

  const tokenServerEndpoint = () => `http://localhost:${serverPort}/token`;

  const setCustomTokenEndpoint = (endpoint: string) => {
    const webAppEnvironment = resolve(
      projectPath,
      'src',
      'environments',
      'environment.ts'
    );
    const portNumberMatcher = /customTokenEndpoint:\s?.*$/m;
    const appEnvironmentFile = readFileSync(webAppEnvironment, 'utf-8');
    const subst = `customTokenEndpoint: '${endpoint}',`;
    const updatedEnvironment = appEnvironmentFile.replace(
      portNumberMatcher,
      subst
    );

    writeFileSync(webAppEnvironment, updatedEnvironment);
  };

  const resetCustomTokenEndpoint = () => setCustomTokenEndpoint('');

  const forceTokenServerPort = (port: number) => {
    const pathToEnv = resolve(projectPath, 'server', '.env');
    const environment = parse(readFileSync(pathToEnv, {encoding: 'utf-8'}));

    const updatedEnvironment = {
      ...environment,
      SERVER_PORT: port,
    };

    truncateSync(pathToEnv);
    for (const [key, value] of Object.entries(updatedEnvironment)) {
      appendFileSync(pathToEnv, `${key}=${value}${EOL}`);
    }
  };

  const forceAppPort = (port: number) => {
    const angularJSON = JSON.parse(readFileSync(angularJsonPath, 'utf-8'));

    const serve = angularJSON.projects[projectName].architect.serve;
    if (!serve.options) {
      serve.options = {};
    }
    serve.options.port = port;

    writeFileSync(angularJsonPath, JSON.stringify(angularJSON, undefined, 2));
  };

  const getAllocatedPorts = () => {
    const envVariables = parse(
      readFileSync(getPathToEnvFile(join(projectPath, 'server')))
    );

    if (!envVariables) {
      throw new Error('Unable to load project environment variables');
    }

    serverPort = parseInt(envVariables.SERVER_PORT);

    const angularJSON = JSON.parse(readFileSync(angularJsonPath, 'utf-8'));
    const servePort =
      angularJSON.projects[projectName].architect.serve.options.port;
    clientPort = parseInt(servePort);

    return [clientPort, serverPort];
  };

  const buildApplication = async (processManager: ProcessManager) => {
    const buildTerminal = await setupUIProject(
      processManager,
      'ui:create:angular',
      projectName,
      {
        flags: ['--defaults'],
        projectDir: projectPath,
      }
    );

    const buildTerminalExitPromise = Promise.race([
      buildTerminal.when('exit').on('process').do().once(),
      buildTerminal
        .when(/Happy hacking!/)
        .on('stdout')
        .do()
        .once(),
    ]);

    await buildTerminal
      .when(isGenericYesNoPrompt)
      .on('stderr')
      .do(answerPrompt(`y${EOL}`))
      .until(buildTerminalExitPromise);
  };

  const startApplication = async (
    processManager: ProcessManager,
    debugName = 'angular-server'
  ) => {
    const args = [...npm(), 'run', 'start'];
    const serverTerminal = new Terminal(
      args.shift()!,
      args,
      {
        cwd: projectPath,
      },
      processManager,
      debugName
    );
    return serverTerminal;
  };

  const waitForAppRunning = (appTerminal: Terminal) =>
    appTerminal
      .when(/Compiled successfully/)
      .on('stdout')
      .do()
      .once();

  beforeAll(async () => {
    await loginWithApiKey(
      process.env.PLATFORM_API_KEY!,
      process.env.ORG_ID!,
      process.env.PLATFORM_ENV!
    );
    const buildProcessManager = new ProcessManager();
    processManagers.push(buildProcessManager);
    browser = await getNewBrowser();
    await buildApplication(buildProcessManager);
    await buildProcessManager.killAllProcesses();
  }, 20 * 60e3);

  beforeEach(async () => {
    jest.resetModules();
    process.env = {...oldEnv};
    page = await openNewPage(browser, page);
  });

  afterEach(async () => {
    await captureScreenshots(browser);
  });

  afterAll(async () => {
    process.env = oldEnv;
    await browser.close();
    await Promise.all(
      processManagers.map((manager) => manager.killAllProcesses())
    );
  });

  describe('when the project is configured correctly', () => {
    let serverProcessManager: ProcessManager;
    let interceptedRequests: HTTPRequest[] = [];
    let consoleInterceptor: BrowserConsoleInterceptor;
    const searchboxSelector = 'app-search-page app-search-box input';

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      const appTerminal = await startApplication(
        serverProcessManager,
        'angular-server-valid'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 15 * 60e3);

    beforeEach(async () => {
      consoleInterceptor = new BrowserConsoleInterceptor(page, projectName);
      await consoleInterceptor.startSession();

      page.on('request', (request: HTTPRequest) => {
        interceptedRequests.push(request);
      });
    });

    afterEach(async () => {
      page.removeAllListeners('request');
      interceptedRequests = [];
      await consoleInterceptor.endSession();
    });

    afterAll(async () => {
      await undoCommit(serverProcessManager, projectPath, projectName);
      await serverProcessManager.killAllProcesses();
    }, 5 * 60e3);
    // TODO CDX-1017: Remove skip
    it.skip(
      'should not contain console errors nor warnings',
      async () => {
        await page.goto(searchPageEndpoint(), {
          waitUntil: 'networkidle2',
        });

        expect(consoleInterceptor.interceptedMessages).toEqual([]);
      },
      5 * 60e3
    );

    it('should contain a search page section', async () => {
      await page.goto(searchPageEndpoint(), {
        waitUntil: 'networkidle2',
      });
      await page.waitForSelector(searchboxSelector);

      expect(await page.$('app-search-page')).not.toBeNull();
    }, 60e3);

    it('should retrieve the search token on the page load', async () => {
      const tokenResponseListener = page.waitForResponse(tokenServerEndpoint());

      page.goto(searchPageEndpoint());
      await page.waitForSelector(searchboxSelector);

      expect(
        JSON.parse(await (await tokenResponseListener).text())
      ).toMatchObject({
        token: expect.stringMatching(jwtTokenPattern),
      });
    }, 60e3);

    it('should send a search query when the page is loaded', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      await page.waitForSelector(searchboxSelector);

      expect(interceptedRequests.some(isSearchRequestOrResponse)).toBeTruthy();
    });

    it('should send a search query on searchbox submit', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      await page.waitForSelector(searchboxSelector);

      interceptedRequests = [];

      await page.focus(searchboxSelector);
      await page.keyboard.type('my query');
      await page.keyboard.press('Enter');

      await retry(async () => {
        expect(
          interceptedRequests.some(isSearchRequestOrResponse)
        ).toBeTruthy();
      });
    }, 60e3);

    it('should be commited without lint-stage errors', async () => {
      const eslintErrorSpy = jest.fn();

      await commitProject(
        serverProcessManager,
        projectPath,
        projectName,
        eslintErrorSpy
      );

      expect(eslintErrorSpy).not.toBeCalled();
    }, 60e3);
  });

  describe('when the .env file is missing', () => {
    let serverProcessManager: ProcessManager;

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      deactivateEnvironmentFile(join(projectPath, 'server'));
    });

    afterAll(async () => {
      restoreEnvironmentFile(join(projectPath, 'server'));
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it(
      'should not start the application',
      async () => {
        const missingEnvErrorSpy = jest.fn();

        const appTerminal = await startApplication(
          serverProcessManager,
          'angular-server-missing-env'
        );

        await appTerminal
          .when(/\.env file not found in the project root/)
          .on('stderr')
          .do(missingEnvErrorSpy)
          .once();

        expect(missingEnvErrorSpy).toHaveBeenCalled();
      },
      2 * 60e3
    );
  });

  describe('when the required environment variables are missing', () => {
    let serverProcessManager: ProcessManager;
    let envFileContent = '';

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      envFileContent = flushEnvFile(join(projectPath, 'server'));
      const appTerminal = await startApplication(
        serverProcessManager,
        'angular-server-invalid'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      overwriteEnvFile(join(projectPath, 'server'), envFileContent);
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it('should redirect the user to an error page', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      expect(page.url()).toEqual(`${searchPageEndpoint()}/error`);
    });
  });

  describe('when the a custom token Endpoint is specified', () => {
    const customTokenEndpoint = 'http://dummyendpoint.com/some-kind-of-path';
    let serverProcessManager: ProcessManager;
    let interceptedRequests: HTTPRequest[] = [];

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      setCustomTokenEndpoint(customTokenEndpoint);

      const appTerminal = await startApplication(
        serverProcessManager,
        'angular-server-port-test'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      await serverProcessManager.killAllProcesses();
      resetCustomTokenEndpoint();
    }, 30e3);

    beforeEach(async () => {
      page.on('request', (request: HTTPRequest) => {
        interceptedRequests.push(request);
      });
    });

    afterEach(async () => {
      page.removeAllListeners('request');
      interceptedRequests = [];
    });

    it('should use the custom token endpoint', async () => {
      page.goto(searchPageEndpoint());

      await retry(async () => {
        expect(
          interceptedRequests.some(
            (request: HTTPRequest) => request.url() === customTokenEndpoint
          )
        ).toBeTruthy();
      });
    }, 10e3);
  });

  describe('when the ports are busy', () => {
    const dummyServers: DummyServer[] = [];
    let serverProcessManager: ProcessManager;
    let usedClientPort: number;
    let usedServerPort: number;

    beforeAll(async () => {
      usedClientPort = await getPort();
      usedServerPort = await getPort();
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      forceAppPort(usedClientPort);
      forceTokenServerPort(usedServerPort);

      dummyServers.push(
        new DummyServer(usedClientPort),
        new DummyServer(usedServerPort)
      );

      const appTerminal = await startApplication(
        serverProcessManager,
        'angular-server-port-test'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      await Promise.all(dummyServers.map((server) => server.close()));
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it('should allocate a new port for the application', async () => {
      expect(clientPort).not.toEqual(usedClientPort);
    });

    it('should not use an undefined port for application', async () => {
      expect(clientPort).not.toBeUndefined();
    });

    it('should allocate a new port for the token server', async () => {
      expect(serverPort).not.toEqual(usedServerPort);
    });

    it('should not use an undefined port for token server', async () => {
      expect(serverPort).not.toBeUndefined();
    });

    it('should run the application on a new port', async () => {
      await expect(
        page.goto(searchPageEndpoint(), {waitUntil: 'load'})
      ).resolves.not.toThrow();
    });

    it('should run the server on a new port', async () => {
      const tokenRequest = await axios.get(tokenServerEndpoint());
      expect(tokenRequest.data.token).toMatch(jwtTokenPattern);
    });
  });
});
Example #22
Source File: cli-aws-authentication-service.spec.ts    From leapp with Mozilla Public License 2.0 4 votes vote down vote up
describe("CliAwsAuthenticationService", () => {
  if (process.env["SKIP_INTEGRATION_TESTS"]) {
    test("Skipping integration tests", () => {});
    return;
  }

  const cases = [true, false];
  test.each(cases)("needAuthentication: %p", async (needAuthentication) => {
    const idpUrl = "https://idpUrl";
    const page = new PageStub(idpUrl, {
      url: () => idpUrl,
      isInterceptResolutionHandled: () => false,
      continue: async () => Promise.resolve(),
    });
    const authenticationService = {
      isAuthenticationUrl: jest.fn(() => needAuthentication),
      isSamlAssertionUrl: jest.fn(() => !needAuthentication),
    };

    const cliAwsAuthenticationService = new CliAwsSamlAuthenticationService(authenticationService as any);
    cliAwsAuthenticationService.getNavigationPage = async (headlessMode: boolean) => {
      expect(headlessMode).toBeTruthy();
      return of(page as unknown as Page).toPromise();
    };

    expect(await cliAwsAuthenticationService.needAuthentication(idpUrl)).toBe(needAuthentication);
    expect(authenticationService.isAuthenticationUrl).toHaveBeenCalledWith(CloudProviderType.aws, idpUrl);
    expect(authenticationService.isSamlAssertionUrl).toHaveBeenCalledWith(CloudProviderType.aws, idpUrl);
    expect(page.onPageCalled).toBeTruthy();
    expect(page.gotoPageCalled).toBeTruthy();
  });

  test("awsSignIn - saml assertion true", async () => {
    const idpUrl = "https://idpUrl";
    const needToAuthenticate = false;
    const page = new PageStub(idpUrl, {
      url: () => "samlUrl",
      isInterceptResolutionHandled: () => false,
      postData: () => "postData",
      continue: async () => Promise.resolve(),
    });

    const authenticationService = {
      isSamlAssertionUrl: jest.fn(() => true),
      extractAwsSamlResponse: (responseHookDetails: any) => {
        expect(responseHookDetails.uploadData[0].bytes.toString()).toBe("postData");

        return "samlResponse";
      },
    };
    const cliAwsAuthenticationService = new CliAwsSamlAuthenticationService(authenticationService as any);
    cliAwsAuthenticationService.getNavigationPage = async (headlessMode: boolean) => {
      expect(headlessMode).toEqual(!needToAuthenticate);
      return of(page as unknown as Page).toPromise();
    };

    const result = await cliAwsAuthenticationService.awsSignIn(idpUrl, needToAuthenticate);
    expect(result).toBe("samlResponse");
    expect(page.onPageCalled).toBeTruthy();
    expect(page.gotoPageCalled).toBeTruthy();
    expect(authenticationService.isSamlAssertionUrl).toHaveBeenCalledWith(CloudProviderType.aws, "samlUrl");
  });

  test("awsSignIn - saml assertion false", async () => {
    const idpUrl = "https://idpUrl";
    const needToAuthenticate = false;
    const page = new PageStub(idpUrl, {
      url: () => "wrongSamlUrl",
      isInterceptResolutionHandled: () => false,
      postData: () => "postData",
      continue: async () => Promise.resolve(),
    });

    let requestCounter = 0;
    const authenticationService = {
      isSamlAssertionUrl: jest.fn(() => {
        requestCounter++;
        if (requestCounter === 1) {
          page.goto(idpUrl).catch(() => null);
          return false;
        } else {
          return true;
        }
      }),
      extractAwsSamlResponse: (responseHookDetails: any) => {
        expect(responseHookDetails.uploadData[0].bytes.toString()).toBe("postData");

        return "samlResponse";
      },
    };

    const cliAwsAuthenticationService = new CliAwsSamlAuthenticationService(authenticationService as any);
    cliAwsAuthenticationService.getNavigationPage = async (headlessMode: boolean) => {
      expect(headlessMode).toEqual(!needToAuthenticate);
      return of(page as unknown as Page).toPromise();
    };

    const result = await cliAwsAuthenticationService.awsSignIn(idpUrl, needToAuthenticate);
    expect(result).toBe("samlResponse");
    expect(page.onPageCalled).toBeTruthy();
    expect(page.gotoPageCalled).toBeTruthy();
    expect(authenticationService.isSamlAssertionUrl).toHaveBeenCalledWith(CloudProviderType.aws, "wrongSamlUrl");
    expect(requestCounter).toBe(2);
  });

  test("getNavigationPage and closeAuthenticationWindow", async () => {
    const cliAwsAuthenticationService = new CliAwsSamlAuthenticationService(null);

    const page = await cliAwsAuthenticationService.getNavigationPage(false);
    const browserProcess = page.browser().process();
    expect(browserProcess).toBeDefined();
    expect(browserProcess?.killed).toBeFalsy();
    expect(browserProcess?.signalCode).toBeNull();

    await cliAwsAuthenticationService.closeAuthenticationWindow();
    expect(browserProcess?.killed).toBeTruthy();
    expect(browserProcess?.signalCode).toEqual("SIGKILL");
  });
});
Example #23
Source File: browser.ts    From cli with Apache License 2.0 4 votes vote down vote up
/**
 * Check if an element is within the viewport or is overlapped by another element or disabled
 * For original source code https://github.com/webdriverio/webdriverio/blob/af968a2bbc2d130d8a63229a8edc4974e92e78c3/packages/webdriverio/src/scripts/isElementClickable.ts
 *
 * @param {Page} page       Puppeteer page to use
 * @param {string} selector Selector to check
 */
export async function isElementClickable(page: Page, selector: string) {
  await page.waitForFunction(
    (sel: string) => {
      const elem = document.querySelector(sel);
      if (
        !elem ||
        !elem.getBoundingClientRect ||
        !elem.scrollIntoView ||
        !elem.contains ||
        !elem.getClientRects ||
        !document.elementFromPoint
      ) {
        return false;
      }

      // Edge before switching to Chromium
      const isOldEdge = !!(window as any).StyleMedia;
      // returns true for Chrome and Firefox and false for Safari, Edge and IE
      const scrollIntoViewFullSupport = !((window as any).safari || isOldEdge);

      // get overlapping element
      function getOverlappingElement(elem: HTMLElement, context?: Document) {
        context = context || document;
        const elemDimension = elem.getBoundingClientRect();
        const x = elemDimension.left + elem.clientWidth / 2;
        const y = elemDimension.top + elem.clientHeight / 2;
        return context.elementFromPoint(x, y);
      }

      // get overlapping element rects (currently only the first)
      // applicable if element's text is multiline.
      function getOverlappingRects(elem: HTMLElement, context?: Document) {
        context = context || document;
        const elems = [];

        const rects = elem.getClientRects();
        // webdriver clicks on center of the first element's rect (line of text), it might change in future
        const rect = rects[0];
        const x = rect.left + rect.width / 2;
        const y = rect.top + rect.height / 2;
        elems.push(context.elementFromPoint(x, y));

        return elems;
      }

      // get overlapping elements
      function getOverlappingElements(elem: HTMLElement, context?: Document) {
        return [getOverlappingElement(elem, context)].concat(
          getOverlappingRects(elem, context)
        );
      }

      // is a node a descendant of a given node
      function nodeContains(elem: HTMLElement, otherNode: HTMLElement) {
        // Edge doesn't support neither Shadow Dom nor contains if ShadowRoot polyfill is used
        if (isOldEdge) {
          let tmpElement = otherNode as HTMLElement | ShadowRoot | Element;
          while (tmpElement) {
            if (tmpElement === elem) {
              return true;
            }

            tmpElement = tmpElement.parentNode as ShadowRoot;
            // DocumentFragment / ShadowRoot polyfill like ShadyRoot
            if (tmpElement && tmpElement.nodeType === 11 && tmpElement.host) {
              tmpElement = tmpElement.host;
            }
          }
          return false;
        }

        return elem.contains(otherNode);
      }

      // is one of overlapping elements the `elem` or one of its child
      function isOverlappingElementMatch(
        elementsFromPoint: HTMLElement[],
        elem: HTMLElement
      ): boolean {
        if (
          elementsFromPoint.some((elementFromPoint) => {
            return (
              elementFromPoint === elem || nodeContains(elem, elementFromPoint)
            );
          })
        ) {
          return true;
        }

        // shadow root
        // filter unique elements with shadowRoot
        let elemsWithShadowRoot = ([] as HTMLElement[]).concat(
          elementsFromPoint
        );
        elemsWithShadowRoot = elemsWithShadowRoot.filter((x: HTMLElement) => {
          return x && x.shadowRoot && (x.shadowRoot as any).elementFromPoint;
        });

        // getOverlappingElements of every element with shadowRoot
        let shadowElementsFromPoint: HTMLElement[] = [];
        for (const shadowElement of elemsWithShadowRoot) {
          shadowElementsFromPoint = shadowElementsFromPoint.concat(
            getOverlappingElements(
              elem,
              (shadowElement as HTMLElement).shadowRoot as any
            ) as any
          );
        }
        // remove duplicates and parents
        shadowElementsFromPoint = ([] as HTMLElement[]).concat(
          shadowElementsFromPoint
        );
        shadowElementsFromPoint = shadowElementsFromPoint.filter((x) => {
          return !elementsFromPoint.includes(x);
        });

        if (shadowElementsFromPoint.length === 0) {
          return false;
        }

        return isOverlappingElementMatch(shadowElementsFromPoint, elem);
      }

      // copied from `isElementInViewport.js`
      function isElementInViewport(elem: HTMLElement) {
        if (!elem.getBoundingClientRect) {
          return false;
        }

        const rect = elem.getBoundingClientRect();

        const windowHeight =
          window.innerHeight || document.documentElement.clientHeight;
        const windowWidth =
          window.innerWidth || document.documentElement.clientWidth;

        const vertInView =
          rect.top <= windowHeight && rect.top + rect.height > 0;
        const horInView =
          rect.left <= windowWidth && rect.left + rect.width > 0;

        return vertInView && horInView;
      }

      function isClickable(elem: any) {
        return (
          isElementInViewport(elem) &&
          elem.disabled !== true &&
          isOverlappingElementMatch(
            getOverlappingElements(elem) as any as HTMLElement[],
            elem
          )
        );
      }

      // scroll to the element if it's not clickable
      if (!isClickable(elem)) {
        // works well in dialogs, but the element may be still overlapped by some sticky header/footer
        elem.scrollIntoView(
          scrollIntoViewFullSupport
            ? {block: 'nearest', inline: 'nearest'}
            : false
        );

        // if element is still not clickable take another scroll attempt
        if (!isClickable(elem)) {
          // scroll to element, try put it in the screen center.
          // Should definitely work even if element was covered with sticky header/footer
          elem.scrollIntoView(
            scrollIntoViewFullSupport
              ? {block: 'center', inline: 'center'}
              : true
          );

          return isClickable(elem);
        }
      }

      return true;
    },
    {},
    selector
  );
}
Example #24
Source File: solver.ts    From FlareSolverr with MIT License 4 votes vote down vote up
async function resolveChallenge(params: V1Request, session: SessionsCacheItem): Promise<ChallengeResolutionT | void> {
    try {
        let status = 'ok'
        let message = ''

        const page: Page = await session.browser.newPage()

        // the Puppeter timeout should be half the maxTimeout because we reload the page and wait for challenge
        // the user can set a really high maxTimeout if he wants to
        await page.setDefaultNavigationTimeout(params.maxTimeout / 2)

        // the user-agent is changed just for linux arm build
        await page.setUserAgent(sessions.getUserAgent())

        // set the proxy
        if (params.proxy) {
            log.debug(`Using proxy: ${params.proxy.url}`);
            // todo: credentials are not working
            // if (params.proxy.username) {
            //     await page.authenticate({
            //         username: params.proxy.username,
            //         password: params.proxy.password
            //     });
            // }
        }

        // go to the page
        log.debug(`Navigating to... ${params.url}`)
        let response: HTTPResponse = await gotoPage(params, page);

        // set cookies
        if (params.cookies) {
            for (const cookie of params.cookies) {
                // the other fields in the cookie can cause issues
                await page.setCookie({
                    "name": cookie.name,
                    "value": cookie.value
                })
            }
            // reload the page
            response = await gotoPage(params, page);
        }

        // log html in debug mode
        log.html(await page.content())

        // detect protection services and solve challenges
        try {
            response = await cloudflareProvider(params.url, page, response);

            // is response is ok
            // reload the page to be sure we get the real page
            log.debug("Reloading the page")
            try {
                response = await gotoPage(params, page);
            } catch (e) {
                log.warn("Page not reloaded (do not report!): Cause: " + e.toString())
            }

        } catch (e) {
            status = "error";
            message = "Cloudflare " + e.toString();
        }

        const payload: ChallengeResolutionT = {
            status,
            message,
            result: {
                url: page.url(),
                status: response.status(),
                headers: response.headers(),
                response: null,
                cookies: await page.cookies(),
                userAgent: sessions.getUserAgent()
            }
        }

        if (params.returnOnlyCookies) {
            payload.result.headers = null;
            payload.result.userAgent = null;
        } else {
            payload.result.response = await page.content()
        }

        // make sure the page is closed because if it isn't and error will be thrown
        // when a user uses a temporary session, the browser make be quit before
        // the page is properly closed.
        await page.close()

        return payload
    } catch (e) {
        log.error("Unexpected error: " + e);
        throw e;
    }
}
Example #25
Source File: serialize-deserialize.test.ts    From web with MIT License 4 votes vote down vote up
describe('serialize deserialize', () => {
  let browser: Browser;
  let page: Page;
  before(async () => {
    browser = await puppeteer.launch();
    page = await browser.newPage();
    await page.goto('about:blank');
    await page.evaluate(
      `(function () { var module = {}; var exports = {}; \n${serializeScript};\n window._serialize = serialize })()`,
    );
  });

  after(async () => {
    await browser.close();
  });

  it('handles strings', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize('foo'));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('foo');
  });

  it('handles numbers', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize(1));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal(1);
  });

  it('handles Date', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(new Date('2020-07-25T12:00:00.000Z')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('2020-07-25T12:00:00.000Z');
  });

  it('handles Function', async () => {
    const serialized = await page.evaluate(() => {
      function foo(x: number, y: number) {
        return x * y;
      }
      return (window as any)._serialize(foo);
    });
    const deserialized = await deserialize(serialized);
    expect(typeof deserialized).to.equal('function');
    expect(deserialized.name).to.equal('foo');
  });

  it('handles bound Function', async () => {
    const serialized = await page.evaluate(() => {
      function foo(x: number, y: number) {
        return x * y;
      }
      return (window as any)._serialize(foo.bind(null));
    });
    const deserialized = await deserialize(serialized);
    expect(typeof deserialized).to.equal('function');
    expect(deserialized.name).to.equal('foo');
  });

  it('handles Symbol', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize(Symbol('foo')));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('Symbol(foo)');
  });

  it('handles arrow functions', async () => {
    const serialized = await page.evaluate(() => {
      const foo = (x: number, y: number) => x * y;
      return (window as any)._serialize(foo);
    });
    const deserialized = await deserialize(serialized);
    expect(typeof deserialized).to.equal('function');
    expect(deserialized.name).to.equal('foo');
  });

  it('handles anonymous arrow functions', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize((x: number, y: number) => x * y),
    );
    const deserialized = await deserialize(serialized);
    expect(typeof deserialized).to.equal('function');
    expect(deserialized.name).to.equal('');
  });

  it('handles Text nodes', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(document.createTextNode('hello world')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('Text: hello world');
  });

  it('handles Comment nodes', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(document.createComment('hello world')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('Comment: hello world');
  });

  it('handles HTMLElement', async () => {
    const serialized = await page.evaluate(() => {
      const element = document.createElement('div');
      element.innerHTML = '<h1><span>Hello world</span></h1>';
      return (window as any)._serialize(element);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('HTMLDivElement: <div><h1><span>Hello world</span></h1></div>');
  });

  it('handles ShadowRoot', async () => {
    const serialized = await page.evaluate(() => {
      const element = document.createElement('div');
      element.attachShadow({ mode: 'open' });
      element.shadowRoot!.innerHTML = '<h1><span>Hello world</span></h1>';
      return (window as any)._serialize(element.shadowRoot);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('ShadowRoot: <h1><span>Hello world</span></h1>');
  });

  it('handles RegExp', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize(/foo.*?\\/));
    const deserialized = await deserialize(serialized);
    expect(deserialized instanceof RegExp).to.be.true;
    expect(deserialized.source).to.equal('foo.*?\\\\');
    expect(deserialized.flags).to.equal('');
  });

  it('handles RegExp with flags', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(new RegExp('foo.*?\\\\', 'g')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized instanceof RegExp).to.be.true;
    expect(deserialized.source).to.equal('foo.*?\\\\');
    expect(deserialized.flags).to.equal('g');
  });

  it('handles URL', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(new URL('https://www.example.com')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('https://www.example.com/');
  });

  it('handles URLSearchparams', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(new URLSearchParams('foo=bar&lorem=ipsum')),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal('URLSearchParams: foo=bar&lorem=ipsum');
  });

  it('handles classes', async () => {
    const serialized = await page.evaluate(() => {
      class Foo {
        a = 1;
        b = 2;
      }
      return (window as any)._serialize(new Foo());
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({ a: 1, b: 2 });
    expect(deserialized.constructor.name).to.equal('Foo');
  });

  it('handles objects', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize({ a: 1, b: 2 }));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({ a: 1, b: 2 });
  });

  it('handles arrays', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize([1, 2, 3]));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql([1, 2, 3]);
  });

  it('handles objects', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize({ a: 1, b: 2 }));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({ a: 1, b: 2 });
  });

  it('handles objects with methods', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize({
        foo() {
          return 'foo';
        },
        bar: () => {
          return 'bar';
        },
        baz: function baz() {
          return 'baz';
        },
        'my-element': () => 'bar',
      }),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized.foo).to.be.a('function');
    expect(deserialized.foo.name).to.equal('foo');
    expect(deserialized.bar).to.be.a('function');
    expect(deserialized.bar.name).to.equal('bar');
    expect(deserialized.baz).to.be.a('function');
    expect(deserialized.baz.name).to.equal('baz');
    expect(deserialized['my-element']).to.be.a('function');
    expect(deserialized['my-element'].name).to.equal('my-element');
  });

  it('handles deep objects', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize({
        myNumber: 123,
        myString: 'foo',
        myObject: {
          myUrl: new URL('http://www.example.com/'),
          myMethod() {
            return 'x';
          },
          myRegExp: /x/,
        },
        myArray: [1, '2'],
      }),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized.myNumber).to.equal(123);
    expect(deserialized.myString).to.equal('foo');
    expect(deserialized.myObject.myUrl).to.equal('http://www.example.com/');
    expect(deserialized.myObject.myMethod).to.be.a('function');
    expect(deserialized.myObject.myMethod.name).to.equal('myMethod');
    expect(deserialized.myObject.myRegExp).to.a('RegExp');
    expect(deserialized.myObject.myRegExp.source).to.equal('x');
    expect(deserialized.myArray).to.eql([1, '2']);
  });

  it('handles deep arrays', async () => {
    const serialized = await page.evaluate(() => {
      class Foo {
        x = 'y';
      }
      return (window as any)._serialize([
        1,
        '2',
        /x/,
        new URL('http://www.example.com/'),
        Symbol('foo'),
        { a: 1, b: 2, c: new URLSearchParams('x=y'), d: new Foo() },
      ]);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized[0]).to.equal(1);
    expect(deserialized[1]).to.equal('2');
    expect(deserialized[2]).to.a('RegExp');
    expect(deserialized[2].source).to.equal('x');
    expect(deserialized[3]).to.equal('http://www.example.com/');
    expect(deserialized[4]).to.equal('Symbol(foo)');
    expect(deserialized[5].a).to.equal(1);
    expect(deserialized[5].b).to.equal(2);
    expect(deserialized[5].c).to.equal('URLSearchParams: x=y');
    expect(deserialized[5].d).to.eql({ x: 'y' });
    expect(deserialized[5].d.constructor.name).to.equal('Foo');
  });

  it('handles circular references', async () => {
    const serialized = await page.evaluate(() => {
      const foo: Record<string, unknown> = {};
      foo.circular = foo;
      return (window as any)._serialize(foo);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({
      circular: '[Circular]',
    });
  });

  it('handles multiple circular references', async () => {
    const serialized = await page.evaluate(() => {
      const foo: Record<string, unknown> = {};
      foo.circular1 = foo;
      foo.x = { circular2: foo, lorem: 'ipsum' };
      foo.y = { z: { a: 1, b: 2, circular3: foo } };
      return (window as any)._serialize(foo);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({
      circular1: '[Circular]',
      x: {
        circular2: '[Circular]',
        lorem: 'ipsum',
      },
      y: {
        z: {
          a: 1,
          b: 2,
          circular3: '[Circular]',
        },
      },
    });
  });

  it('handles circular references in arrays', async () => {
    const serialized = await page.evaluate(() => {
      const foo: Record<string, unknown> = {};
      foo.circulars = [foo, 'bar', foo];
      return (window as any)._serialize(foo);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({ circulars: ['[Circular]', 'bar', '[Circular]'] });
  });

  it('handles generated circular references', async () => {
    const serialized = await page.evaluate(() => {
      const Foo = () => null;
      const obj: any = { f: Foo, x: null };
      obj.x = obj;
      return (window as any)._serialize(obj);
    });
    const deserialized = await deserialize(serialized);
    expect(deserialized.f).to.be.a('function');
    expect(deserialized.x).to.equal('[Circular]');
  });

  it('handles errors', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize(a());
    });
    const deserialized = await deserialize(serialized, defaultOptions);
    expect(deserialized).to.be.a('string');
    expect(deserialized).to.include('my error msg');
    expect(deserialized).to.include('  at c (__puppeteer_evaluation_script__:2:29)');
    expect(deserialized).to.include('  at b (__puppeteer_evaluation_script__:3:29)');
    expect(deserialized).to.include('  at a (__puppeteer_evaluation_script__:4:29)');
    expect(deserialized).to.include('  at __puppeteer_evaluation_script__:5:38');
  });

  it('handles errors in objects', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize({ myError: a() });
    });
    const deserialized = await deserialize(serialized, defaultOptions);
    expect(deserialized.myError).to.be.a('string');
    expect(deserialized.myError).to.include('my error msg');
    expect(deserialized.myError).to.include('  at c (__puppeteer_evaluation_script__:2:29)');
    expect(deserialized.myError).to.include('  at b (__puppeteer_evaluation_script__:3:29)');
    expect(deserialized.myError).to.include('  at a (__puppeteer_evaluation_script__:4:29)');
    expect(deserialized.myError).to.include('at __puppeteer_evaluation_script__:5:49');
  });

  it('handles errors in arrays', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize([a(), b(), c()]);
    });
    const deserialized = await deserialize(serialized, defaultOptions);
    expect(deserialized[0]).to.be.a('string');
    expect(deserialized[0]).to.include('my error msg');
    expect(deserialized[0]).to.include('  at c (__puppeteer_evaluation_script__:2:29)');
    expect(deserialized[0]).to.include('  at b (__puppeteer_evaluation_script__:3:29)');
    expect(deserialized[0]).to.include('  at a (__puppeteer_evaluation_script__:4:29)');
    expect(deserialized[0]).to.include('at __puppeteer_evaluation_script__:5:39');
    expect(deserialized[1]).to.be.a('string');
    expect(deserialized[1]).to.include('my error msg');
    expect(deserialized[1]).to.include('  at c (__puppeteer_evaluation_script__:2:29)');
    expect(deserialized[1]).to.include('  at b (__puppeteer_evaluation_script__:3:29)');
    expect(deserialized[1]).to.include('at __puppeteer_evaluation_script__:5:44');
    expect(deserialized[2]).to.be.a('string');
    expect(deserialized[2]).to.include('my error msg');
    expect(deserialized[2]).to.include('  at c (__puppeteer_evaluation_script__:2:29)');
    expect(deserialized[2]).to.include('at __puppeteer_evaluation_script__:5:49');
  });

  it('can map stack trace locations', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize(a());
    });
    const deserialized = await deserialize(serialized, {
      ...defaultOptions,
      mapStackLocation: l => ({ ...l, filePath: `${l.filePath}__MAPPED__`, line: 1, column: 2 }),
    });
    expect(deserialized).to.be.a('string');
    expect(deserialized).to.include('my error msg');
    expect(deserialized).to.include('  at c (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at b (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at a (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at __puppeteer_evaluation_script____MAPPED__:1:2');
  });

  it('mapped stack traces can be async', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize(a());
    });
    const deserialized = await deserialize(serialized, {
      ...defaultOptions,
      async mapStackLocation(l) {
        await new Promise(r => setTimeout(r, 100));
        return { ...l, filePath: `${l.filePath}__MAPPED__`, line: 1, column: 2 };
      },
    });
    expect(deserialized).to.be.a('string');
    expect(deserialized).to.include('my error msg');
    expect(deserialized).to.include('  at c (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at b (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at a (__puppeteer_evaluation_script____MAPPED__:1:2)');
    expect(deserialized).to.include('  at __puppeteer_evaluation_script____MAPPED__:1:2');
  });

  it('can define a cwd below current directory', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize(a());
    });
    const deserialized = await deserialize(serialized, {
      ...defaultOptions,
      cwd: path.resolve(__dirname, '..'),
    });
    expect(deserialized).to.be.a('string');
    expect(deserialized).to.include('my error msg');
    expect(deserialized).to.include(`  at c (test${sep}__puppeteer_evaluation_script__:2:29)`);
    expect(deserialized).to.include(`  at b (test${sep}__puppeteer_evaluation_script__:3:29)`);
    expect(deserialized).to.include(`  at a (test${sep}__puppeteer_evaluation_script__:4:29)`);
    expect(deserialized).to.include(`  at test${sep}__puppeteer_evaluation_script__:5:38`);
  });

  it('can define a cwd above current directory', async () => {
    const serialized = await page.evaluate(() => {
      const c = () => new Error('my error msg');
      const b = () => c();
      const a = () => b();
      return (window as any)._serialize(a());
    });
    const deserialized = await deserialize(serialized, {
      cwd: path.resolve(__dirname, '..', 'foo'),
      browserRootDir: path.resolve(__dirname, '..'),
    });
    expect(deserialized).to.be.a('string');
    expect(deserialized).to.include('my error msg');
    expect(deserialized).to.include(`  at c (..${sep}__puppeteer_evaluation_script__:2:29)`);
    expect(deserialized).to.include(`  at b (..${sep}__puppeteer_evaluation_script__:3:29)`);
    expect(deserialized).to.include(`  at a (..${sep}__puppeteer_evaluation_script__:4:29)`);
    expect(deserialized).to.include(`  at ..${sep}__puppeteer_evaluation_script__:5:38`);
  });

  it('handles null', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize(null));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal(null);
  });

  it('handles undefined', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize(undefined));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.equal(undefined);
  });

  it('handles undefined in an object', async () => {
    const serialized = await page.evaluate(() => (window as any)._serialize({ x: undefined }));
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({ x: undefined });
  });

  it('handles undefined in an array', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize([1, undefined, '2', undefined]),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql([1, undefined, '2', undefined]);
  });

  it('handles multiple undefined values', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize({
        a: { a1: undefined, a2: undefined, a3: { x: undefined } },
        b: undefined,
        c: { q: [1, undefined] },
      }),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql({
      a: { a1: undefined, a2: undefined, a3: { x: undefined } },
      b: undefined,
      c: { q: [1, undefined] },
    });
  });

  it('handles Promises', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize(new Promise(resolve => resolve(1))),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql('Promise { }');
  });

  it('handles errors thrown during serialization', async () => {
    const serialized = await page.evaluate(() =>
      (window as any)._serialize({
        get x() {
          throw new Error('error in getter');
        },
      }),
    );
    const deserialized = await deserialize(serialized);
    expect(deserialized).to.eql(null);
  });
});
Example #26
Source File: cloudflare.ts    From FlareSolverr with MIT License 4 votes vote down vote up
export default async function resolveChallenge(url: string, page: Page, response: HTTPResponse): Promise<HTTPResponse> {

  // look for challenge and return fast if not detected
  let cfDetected = response.headers().server &&
      (response.headers().server.startsWith('cloudflare') || response.headers().server.startsWith('ddos-guard'));
  if (cfDetected) {
    if (response.status() == 403 || response.status() == 503) {
      cfDetected = true; // Defected CloudFlare and DDoS-GUARD
    } else if (response.headers().vary && response.headers().vary.trim() == 'Accept-Encoding,User-Agent' &&
        response.headers()['content-encoding'] && response.headers()['content-encoding'].trim() == 'br') {
      cfDetected = true; // Detected Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands
    } else {
      cfDetected = false;
    }
  }

  if (cfDetected) {
    log.info('Cloudflare detected');
  } else {
    log.info('Cloudflare not detected');
    return response;
  }

  if (await findAnySelector(page, BAN_SELECTORS)) {
    throw new Error('Cloudflare has blocked this request. Probably your IP is banned for this site, check in your web browser.');
  }

  // find Cloudflare selectors
  let selectorFound = false;
  let selector: string = await findAnySelector(page, CHALLENGE_SELECTORS)
  if (selector) {
    selectorFound = true;
    log.debug(`Javascript challenge element '${selector}' detected.`)
    log.debug('Waiting for Cloudflare challenge...')

    while (true) {
      try {

        selector = await findAnySelector(page, CHALLENGE_SELECTORS)
        if (!selector) {
          // solved!
          log.debug('Challenge element not found')
          break
        } else {
          log.debug(`Javascript challenge element '${selector}' detected.`)

          // new Cloudflare Challenge #cf-please-wait
          const displayStyle = await page.evaluate((selector) => {
            return getComputedStyle(document.querySelector(selector)).getPropertyValue("display");
          }, selector);
          if (displayStyle == "none") {
            // spinner is hidden, could be a captcha or not
            log.debug('Challenge element is hidden')
            // wait until redirecting disappears
            while (true) {
              try {
                await page.waitForTimeout(1000)
                const displayStyle2 = await page.evaluate(() => {
                  return getComputedStyle(document.querySelector('#cf-spinner-redirecting')).getPropertyValue("display");
                });
                if (displayStyle2 == "none") {
                  break // hCaptcha detected
                }
              } catch (error) {
                break // redirection completed
              }
            }
            break
          } else {
            log.debug('Challenge element is visible')
          }
        }
        log.debug('Found challenge element again')

      } catch (error)
      {
        log.debug("Unexpected error: " + error);
        if (!error.toString().includes("Execution context was destroyed")) {
          break
        }
      }

      log.debug('Waiting for Cloudflare challenge...')
      await page.waitForTimeout(1000)
    }

    log.debug('Validating HTML code...')
  } else {
    log.debug(`No challenge element detected.`)
  }

  // check for CAPTCHA challenge
  if (await findAnySelector(page, CAPTCHA_SELECTORS)) {
    log.info('CAPTCHA challenge detected');
    throw new Error('FlareSolverr can not resolve CAPTCHA challenges. Since the captcha doesn\'t always appear, you may have better luck with the next request.');

    // const captchaSolver = getCaptchaSolver()
    // if (captchaSolver) {
    //     // to-do: get the params
    //     log.info('Waiting to receive captcha token to bypass challenge...')
    //     const token = await captchaSolver({
    //       url,
    //       sitekey,
    //       type: captchaType
    //     })
    //     log.debug(`Token received: ${token}`);
    //     // to-do: send the token
    //   }
    // } else {
    //   throw new Error('Captcha detected but no automatic solver is configured.');
    // }
  } else {
    if (!selectorFound)
    {
      throw new Error('No challenge selectors found, unable to proceed.')
    } else {
      log.info('Challenge solved');
    }
  }

  return response;
}
Example #27
Source File: vue.specs.ts    From cli with Apache License 2.0 4 votes vote down vote up
describe('ui:create:vue', () => {
  let browser: Browser;
  const processManagers: ProcessManager[] = [];
  let page: Page;
  const oldEnv = process.env;
  const parentDir = 'vue';
  const projectName = `${process.env.TEST_RUN_ID}-${parentDir}-project`;
  const projectPath = join(getUIProjectPath(), parentDir, projectName);
  let clientPort: number;
  let serverPort: number;

  const searchPageEndpoint = () => `http://localhost:${clientPort}`;

  const tokenServerEndpoint = () => `http://localhost:${serverPort}/token`;

  const forceApplicationPorts = (clientPort: number, serverPort: number) => {
    const envPath = getPathToEnvFile(projectPath);
    const environment = parse(readFileSync(envPath, {encoding: 'utf-8'}));

    const updatedEnvironment = {
      ...environment,
      PORT: clientPort,
      VUE_APP_SERVER_PORT: serverPort,
    };
    truncateSync(envPath);
    for (const [key, value] of Object.entries(updatedEnvironment)) {
      appendFileSync(envPath, `${key}=${value}${EOL}`);
    }
  };

  const waitForAppRunning = (appTerminal: Terminal) =>
    appTerminal
      .when(/App running at:/)
      .on('stdout')
      .do()
      .once();

  const getAllocatedPorts = () => {
    const envVariables = parse(
      readFileSync(getPathToEnvFile(projectPath), {encoding: 'utf-8'})
    );

    if (!envVariables) {
      throw new Error('Unable to load project environment variables');
    }

    clientPort = parseInt(envVariables.PORT);
    serverPort = parseInt(envVariables.VUE_APP_SERVER_PORT);

    return [clientPort, serverPort];
  };

  const buildApplication = async (processManager: ProcessManager) => {
    const buildTerminal = await setupUIProject(
      processManager,
      'ui:create:vue',
      projectName,
      {projectDir: projectPath}
    );

    const buildTerminalExitPromise = Promise.race([
      buildTerminal.when('exit').on('process').do().once(),
      buildTerminal
        .when(/Happy hacking!/)
        .on('stdout')
        .do()
        .once(),
    ]);

    await buildTerminal
      .when(isGenericYesNoPrompt)
      .on('stdout')
      .do(answerPrompt(`y${EOL}`))
      .until(buildTerminalExitPromise);

    await buildTerminalExitPromise;
  };

  const startApplication = async (
    processManager: ProcessManager,
    debugName = 'vue-server'
  ) => {
    const args = [...npm(), 'run', 'start'];

    const serverTerminal = new Terminal(
      args.shift()!,
      args,
      {
        cwd: projectPath,
      },
      processManager,
      debugName
    );
    return serverTerminal;
  };

  beforeAll(async () => {
    await loginWithApiKey(
      process.env.PLATFORM_API_KEY!,
      process.env.ORG_ID!,
      process.env.PLATFORM_ENV!
    );
    const buildProcessManager = new ProcessManager();
    processManagers.push(buildProcessManager);
    browser = await getNewBrowser();
    await buildApplication(buildProcessManager);
    await buildProcessManager.killAllProcesses();
  }, 15 * 60e3);

  beforeEach(async () => {
    jest.resetModules();
    process.env = {...oldEnv};
    page = await openNewPage(browser, page);
  });

  afterEach(async () => {
    await captureScreenshots(browser);
  });

  afterAll(async () => {
    process.env = oldEnv;
    await browser.close();
    await Promise.all(
      processManagers.map((manager) => manager.killAllProcesses())
    );
  });

  describe('when the project is configured correctly', () => {
    let serverProcessManager: ProcessManager;
    let interceptedRequests: HTTPRequest[] = [];
    let consoleInterceptor: BrowserConsoleInterceptor;
    const searchboxSelector = '#search-page .autocomplete input';

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      const appTerminal = await startApplication(
        serverProcessManager,
        'vue-server-valid'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    beforeEach(async () => {
      consoleInterceptor = new BrowserConsoleInterceptor(page, projectName);
      await consoleInterceptor.startSession();

      page.on('request', (request: HTTPRequest) => {
        interceptedRequests.push(request);
      });
    });

    afterEach(async () => {
      page.removeAllListeners('request');
      interceptedRequests = [];
      await consoleInterceptor.endSession();
    });

    afterAll(async () => {
      await undoCommit(serverProcessManager, projectPath, projectName);
      await serverProcessManager.killAllProcesses();
    }, 5 * 60e3);
    // TODO CDX-1017: Remove skip
    it.skip('should not contain console errors nor warnings', async () => {
      await page.goto(searchPageEndpoint(), {
        waitUntil: 'networkidle2',
      });

      expect(consoleInterceptor.interceptedMessages).toEqual([]);
    }, 60e3);

    it('should contain a search page section', async () => {
      await page.goto(searchPageEndpoint(), {
        waitUntil: 'networkidle2',
      });
      await page.waitForSelector(searchboxSelector);

      expect(await page.$('#search-page')).not.toBeNull();
    }, 60e3);

    it('should retrieve the search token on the page load', async () => {
      const tokenResponseListener = page.waitForResponse(tokenServerEndpoint());

      page.goto(searchPageEndpoint());
      await page.waitForSelector(searchboxSelector);

      expect(
        JSON.parse(await (await tokenResponseListener).text())
      ).toMatchObject({
        token: expect.stringMatching(jwtTokenPattern),
      });
    }, 60e3);

    it('should send a search query when the page is loaded', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      await page.waitForSelector(searchboxSelector);

      expect(interceptedRequests.some(isSearchRequestOrResponse)).toBeTruthy();
    }, 60e3);

    it('should send a search query on searchbox submit', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      await page.waitForSelector(searchboxSelector);

      interceptedRequests = [];

      await page.focus(searchboxSelector);
      await page.keyboard.type('my query');
      await page.keyboard.press('Enter');

      await retry(async () => {
        expect(
          interceptedRequests.some(isSearchRequestOrResponse)
        ).toBeTruthy();
      });
    }, 60e3);

    it('should be commited without lint-stage errors', async () => {
      const eslintErrorSpy = jest.fn();

      await commitProject(
        serverProcessManager,
        projectPath,
        projectName,
        eslintErrorSpy
      );

      expect(eslintErrorSpy).not.toBeCalled();
    }, 10e3);
  });

  describe('when starting the server', () => {
    let serverProcessManager: ProcessManager;

    beforeEach(() => {
      serverProcessManager = new ProcessManager();
    });

    afterEach(async () => {
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it(
      'should not have any ESLint warning or error',
      async () => {
        const serverTerminal = await startApplication(
          serverProcessManager,
          'vue-server-eslint'
        );
        const eslintErrorSpy = jest.fn();

        await serverTerminal
          .when(/✖ \d+ problems \(\d+ errors, \d+ warnings\)/)
          .on('stdout')
          .do(eslintErrorSpy)
          .until(waitForAppRunning(serverTerminal));

        expect(eslintErrorSpy).not.toBeCalled();
      },
      5 * 60e3
    );
  });

  describe('when the .env file is missing', () => {
    let serverProcessManager: ProcessManager;

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      deactivateEnvironmentFile(projectPath);
    });

    afterAll(async () => {
      await serverProcessManager.killAllProcesses();
      restoreEnvironmentFile(projectPath);
    }, 30e3);

    it(
      'should not start the application',
      async () => {
        const missingEnvErrorSpy = jest.fn();

        const appTerminal = await startApplication(
          serverProcessManager,
          'vue-server-missing-env'
        );

        await appTerminal
          .when(/\.env file not found in the project root/)
          .on('stderr')
          .do(missingEnvErrorSpy)
          .once();

        expect(missingEnvErrorSpy).toHaveBeenCalled();
      },
      2 * 60e3
    );
  });

  describe('when required environment variables are not defined', () => {
    let serverProcessManager: ProcessManager;
    let envFileContent = '';

    beforeAll(async () => {
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      envFileContent = flushEnvFile(projectPath);
      const appTerminal = await startApplication(
        serverProcessManager,
        'vue-server-invalid'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      overwriteEnvFile(projectPath, envFileContent);
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it('should redirect the user to an error page', async () => {
      await page.goto(searchPageEndpoint(), {waitUntil: 'networkidle2'});
      expect(page.url()).toEqual(`${searchPageEndpoint()}/error`);
    }, 60e3);
  });

  describe('when the ports are manually specified', () => {
    let serverProcessManager: ProcessManager;
    let hardCodedClientPort: number;
    let hardCodedServerPort: number;

    beforeAll(async () => {
      hardCodedClientPort = await getPort();
      hardCodedServerPort = await getPort();
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      forceApplicationPorts(hardCodedClientPort, hardCodedServerPort);

      const appTerminal = await startApplication(
        serverProcessManager,
        'vue-server-port-test'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it('should run the application on the specified port', async () => {
      expect(clientPort).toEqual(hardCodedClientPort);
    }, 60e3);

    it('should run the token server on the specified port', async () => {
      expect(serverPort).toEqual(hardCodedServerPort);
    }, 60e3);
  });

  describe('when the ports are busy', () => {
    const dummyServers: DummyServer[] = [];
    let serverProcessManager: ProcessManager;
    let usedClientPort: number;
    let usedServerPort: number;

    beforeAll(async () => {
      usedClientPort = await getPort();
      usedServerPort = await getPort();
      serverProcessManager = new ProcessManager();
      processManagers.push(serverProcessManager);
      forceApplicationPorts(usedClientPort, usedServerPort);

      dummyServers.push(
        new DummyServer(usedClientPort),
        new DummyServer(usedServerPort)
      );

      const appTerminal = await startApplication(
        serverProcessManager,
        'vue-server-port-test'
      );
      await waitForAppRunning(appTerminal);
      [clientPort, serverPort] = getAllocatedPorts();
    }, 2 * 60e3);

    afterAll(async () => {
      await Promise.all(dummyServers.map((server) => server.close()));
      await serverProcessManager.killAllProcesses();
    }, 30e3);

    it('should allocate a new port for the application', async () => {
      expect(clientPort).not.toEqual(usedClientPort);
    });

    it('should not use an undefined port for application', async () => {
      expect(clientPort).not.toBeUndefined();
    });

    it('should allocate a new port for the token server', async () => {
      expect(serverPort).not.toEqual(usedServerPort);
    });

    it('should not use an undefined port for token server', async () => {
      expect(serverPort).not.toBeUndefined();
    });

    it('should run the application on a new port', async () => {
      await expect(
        page.goto(searchPageEndpoint(), {waitUntil: 'load'})
      ).resolves.not.toThrow();
    });

    it('should run the server on a new port', async () => {
      const tokenRequest = await axios.get(tokenServerEndpoint());
      expect(tokenRequest.data.token).toMatch(jwtTokenPattern);
    });
  });

  it.todo('should create a Vue.js project with a custom preset');
});