UNPKG

screenshotone-validations

Version:

Validation schemes for the ScreenshotOne API requests.

466 lines (389 loc) 15.6 kB
import { describe, expect, test } from "@jest/globals"; import validationSchemes, { validateRequest } from "./main"; describe("request validator", () => { test("returns validated value for a valid request", async () => { const { error, value } = validationSchemes.take.validateGet({ url: "https://example.com", }); expect(error).toBeUndefined(); expect(value.url).toBe("https://example.com"); expect(value.format).toBe("jpg"); }); test("returns error and validated value for an invalid request", async () => { const { error, value } = validateRequest( validationSchemes.take.getScheme, { url: "https://" }, ); expect(error).toBeDefined(); expect(error?.message).toContain("must be a valid URI"); expect(value.url).toBe("https://"); }); test("returns a clear error when full_page_scroll is specified without full_page", async () => { const { error, value } = validationSchemes.take.validateGet({ url: "https://example.com", full_page: false, full_page_scroll: true, }); expect(error).toEqual({ message: '"full_page_scroll" is not allowed', }); expect(value.full_page_scroll).toBe(true); }); }); describe("URL validations", () => { test("https://https://example.com must be invalid", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("http://https://example.com must be invalid", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "http://https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("https:// must be invalid", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://" }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("https://example.com must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("https://example.com:80 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://example.com:80" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("https://1.1.1.1:80 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://1.1.1.1:80" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("https://1.1.1.1 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://1.1.1.1" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("proxy with http://example.com must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://example.com", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("proxy with http://example.com:8080 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://example.com:8080", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("proxy with http://1.1.1.1 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://1.1.1.1", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("proxy with http://1.1.1.1:8080 must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://1.1.1.1:8080", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("proxy with https://example.com must be invalid", async () => { const { error } = validationSchemes.take.getScheme.validate( { proxy: "https://example.com", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("proxy with invalid URL must be invalid", async () => { const { error } = validationSchemes.take.getScheme.validate( { proxy: "not-a-url", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("proxy with auth credentials must be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://user:pass@example.com:8080", url: "https://example.com", }, validationSchemes.take.validationOptions, ); expect(() => new URL(value.proxy)).not.toThrow(); expect(error).toBeUndefined(); }); test("proxy with unicode characters should be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://münchen.de", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(() => new URL(value.proxy)).not.toThrow(); expect(error).toBeUndefined(); }); test("proxy with spaces should not be valid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://proxy server.com", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(() => new URL(value.proxy)).toThrow(); expect(error).toBeDefined(); }); test("proxy with unescaped brackets must invalid", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { proxy: "http://proxy[1].com", url: "https://example.com" }, validationSchemes.take.validationOptions, ); expect(() => new URL(value.proxy)).toThrow(); expect(error).toBeDefined(); }); }); describe("Proxy bypass host validations", () => { test("exact and wildcard hostname patterns must be valid", async () => { const proxyBypassHosts = [ "cdn.example.com", "cdn.example.screenshotone.com", "cdn.*.screenshotone.com", ]; const { error, value } = validationSchemes.take.validateGet({ url: "https://example.com", proxy: "http://example.com:8080", proxy_bypass_hosts: proxyBypassHosts, }); expect(error).toBeUndefined(); expect(value.proxy_bypass_hosts).toEqual(proxyBypassHosts); }); test("proxy_bypass_hosts requires proxy", async () => { const { error } = validationSchemes.take.validateGet({ url: "https://example.com", proxy_bypass_hosts: ["cdn.example.com"], }); expect(error?.message).toContain( 'requires the "proxy" option to be specified', ); }); test("proxy_bypass_hosts defaults to an empty list when proxy is specified", async () => { const { error, value } = validationSchemes.take.validateGet({ url: "https://example.com", proxy: "http://example.com:8080", }); expect(error).toBeUndefined(); expect(value.proxy_bypass_hosts).toEqual([]); }); test("a full URL must not be a valid proxy bypass host", async () => { const { error } = validationSchemes.take.validateGet({ url: "https://example.com", proxy: "http://example.com:8080", proxy_bypass_hosts: ["https://cdn.example.com/resource.js"], }); expect(error?.message).toContain( "must contain only hostname patterns", ); }); test("a hostname with a port must not be a valid proxy bypass host", async () => { const { error } = validationSchemes.take.validateGet({ url: "https://example.com", proxy: "http://example.com:8080", proxy_bypass_hosts: ["cdn.example.com:443"], }); expect(error?.message).toContain( "must contain only hostname patterns", ); }); }); describe("Full page slices validations", () => { test("full_page_slices requires full_page", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page_slices: true, response_type: "json", }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("full_page_slices allows by_format response type", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "by_format", }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); expect(value.response_type).toBe("by_format"); }); test("full_page_slices rejects store", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", store: true, storage_path: "test", }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("full_page_slice_height must be positive", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", full_page_slice_height: 0, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("full_page_slice_overlap_height must be non-negative", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", full_page_slice_overlap_height: -1, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("full_page_slice_overlap_height must leave at least 100px step", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", full_page_slice_height: 4000, full_page_slice_overlap_height: 3901, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("full_page_slice_overlap_height can be zero", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", full_page_slice_height: 4000, full_page_slice_overlap_height: 0, }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); expect(value.full_page_slice_overlap_height).toBe(0); }); test("full_page_slices defaults slice options", async () => { const { error, value } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, full_page_slices: true, response_type: "json", }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); expect(value.full_page_slice_height).toBe(4000); expect(value.full_page_slice_overlap_height).toBe(0); }); }); describe("Clip validations", () => { test("full_page is valid without clip options", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); test("clip and full_page are mutually exclusive", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, clip_x: 0, clip_y: 0, clip_width: 800, clip_height: 600, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test("clip options set to zero and full_page are mutually exclusive", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, clip_x: 0, clip_y: 0, clip_width: 0, clip_height: 0, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }); test.each(["clip_x", "clip_y", "clip_width", "clip_height"])( "%s and full_page are mutually exclusive", async (clipOption) => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", full_page: true, [clipOption]: 0, }, validationSchemes.take.validationOptions, ); expect(error).toBeDefined(); }, ); test("clip is valid without full_page", async () => { const { error } = validationSchemes.take.getScheme.validate( { url: "https://example.com", clip_x: 0, clip_y: 0, clip_width: 800, clip_height: 600, }, validationSchemes.take.validationOptions, ); expect(error).toBeUndefined(); }); });