SergeiKiss/backend-project-4

View on GitHub
__tests__/main.test.js

Summary

Maintainability
A
0 mins
Test Coverage
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';
import {
  beforeEach, test, expect, afterAll, jest, describe,
} from '@jest/globals';
import nock from 'nock';
import * as prettier from 'prettier';
import loadPage from '../src/index.js';

// loadPage(url, outputPath)

nock.disableNetConnect();

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const getFixturePath = (filename) => path.join(__dirname, '.', '__fixtures__', filename);

const noop = () => {};
const beforePath = getFixturePath('before.html');
const afterPath = getFixturePath('after.html');
const imagePath = getFixturePath('nodejs.png');
let currentDir;
let rawHTML;
let expectedHTML;
let expectedImage;

beforeEach(async () => {
  await fs.rm(currentDir, { recursive: true }).catch(noop);
  currentDir = await fs.mkdtemp(path.join(os.tmpdir(), 'page-loader-'));
  rawHTML = await fs.readFile(beforePath, 'utf-8');
  expectedHTML = await fs.readFile(afterPath, 'utf-8')
    .then(async (html) => {
      const prettierConfig = await prettier.resolveConfig(afterPath);
      return prettier.format(html, { ...prettierConfig, filepath: afterPath });
    });
  expectedImage = await fs.readFile(imagePath);
});

describe('general cases', () => {
  test('page-loader - basic case', async () => {
    const url = new URL('https://ru.hexlet.io/courses');
    nock(url.origin)
      .get(url.pathname)
      .reply(200, rawHTML);

    nock(url.origin)
      .get(url.pathname)
      .reply(200, rawHTML);

    const imgURL = new URL('https://ru.hexlet.io/assets/professions/nodejs.png');
    nock(imgURL.origin)
      .get(imgURL.pathname)
      .reply(200, expectedImage);

    const stylesheetURL = new URL('https://ru.hexlet.io/assets/application.css');
    nock(stylesheetURL.origin)
      .get(stylesheetURL.pathname)
      .reply(200, 'h3 { font-weight: normal; }');

    const scriptURL = new URL('https://ru.hexlet.io/packs/js/runtime.js');
    nock(scriptURL.origin)
      .get(scriptURL.pathname)
      .reply(200, "console.log('Hello, World!')");

    const logSpy = jest.spyOn(console, 'log');

    await loadPage(url.href, currentDir);
    const expectedMainFilePath = `${currentDir}/ru-hexlet-io-courses.html`;
    const actualData = await fs.readFile(expectedMainFilePath, 'utf-8')
      .then(async (html) => {
        const prettierConfig = await prettier.resolveConfig(afterPath);
        return prettier.format(html, { ...prettierConfig, filepath: afterPath });
      });

    expect(actualData).toEqual(expectedHTML);
    expect(logSpy).toHaveBeenCalledWith(`Page was successfully downloaded into '${expectedMainFilePath}'`);

    const expectedFilesDirPath = path.resolve(currentDir, 'ru-hexlet-io-courses_files');
    await expect(fs.opendir(expectedFilesDirPath))
      .resolves.not.toThrow();

    const imgName = 'ru-hexlet-io-assets-professions-nodejs.png';
    const stylesheetName = 'ru-hexlet-io-assets-application.css';
    const scriptName = 'ru-hexlet-io-packs-js-runtime.js';
    const contentPaths = await fs.readdir(expectedFilesDirPath);
    expect(contentPaths.includes(imgName)).toBeTruthy();
    expect(contentPaths.includes(stylesheetName)).toBeTruthy();
    expect(contentPaths.includes(scriptName)).toBeTruthy();

    const imgPath = path.join(expectedFilesDirPath, imgName);
    const actualImage = await fs.readFile(imgPath);
    expect(actualImage).toEqual(expectedImage);

    const stylesheetPath = path.join(expectedFilesDirPath, stylesheetName);
    const actualStylesheet = await fs.readFile(stylesheetPath, 'utf-8');
    expect(actualStylesheet).toEqual('h3 { font-weight: normal; }');

    const scriptPath = path.join(expectedFilesDirPath, scriptName);
    const actualScript = await fs.readFile(scriptPath, 'utf-8');
    expect(actualScript).toEqual("console.log('Hello, World!')");
  });
});

describe('error cases', () => {
  const url = new URL('https://ru.hexlet.io/courses');

  test('network error - 404', async () => {
    nock(url.origin)
      .get(url.pathname)
      .reply(404);

    await expect(loadPage(url.href, currentDir))
      .rejects.toThrow('Request failed with status code 404');
  });

  test('network error - 500', async () => {
    nock(url.origin)
      .get(url.pathname)
      .reply(500);

    await expect(loadPage(url.href, currentDir))
      .rejects.toThrow('Request failed with status code 500');
  });

  test('file system error - access is denied', async () => {
    nock(url.origin)
      .get(url.pathname)
      .reply(200, rawHTML);

    nock(url.origin)
      .get(url.pathname)
      .reply(200, rawHTML);

    const imgURL = new URL('https://ru.hexlet.io/assets/professions/nodejs.png');
    nock(imgURL.origin)
      .get(imgURL.pathname)
      .reply(200, expectedImage);

    const stylesheetURL = new URL('https://ru.hexlet.io/assets/application.css');
    nock(stylesheetURL.origin)
      .get(stylesheetURL.pathname)
      .reply(200, 'h3 { font-weight: normal; }');

    const scriptURL = new URL('https://ru.hexlet.io/packs/js/runtime.js');
    nock(scriptURL.origin)
      .get(scriptURL.pathname)
      .reply(200, "console.log('Hello, World!')");

    await fs.chmod(currentDir, 0o400);
    await expect(loadPage(url.href, currentDir))
      .rejects.toThrow(/EACCES/);
  });

  test("file system error - output directory doesn't exist", async () => {
    const badPath = path.join(currentDir, 'unknown');
    await expect(loadPage(url.href, badPath))
      .rejects.toThrow(/ENOENT/);
  });
});

afterAll(() => {
  fs.rm(currentDir, { recursive: true }).catch(noop);
});