Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9894213393 | ||
|
|
80523c5a59 | ||
|
|
65b5ff4ec0 | ||
|
|
4e31d0433e | ||
|
|
ed82b96152 | ||
|
|
f16a2a9ed5 | ||
|
|
38d7873d6e | ||
|
|
d848179a43 | ||
|
|
399f515eeb | ||
|
|
46616f327b | ||
|
|
090d647390 | ||
|
|
4fa88b7aba | ||
|
|
8fbae735fa |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -623,7 +623,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monolith"
|
||||
version = "2.2.6"
|
||||
version = "2.2.7"
|
||||
dependencies = [
|
||||
"assert_cmd 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monolith"
|
||||
version = "2.2.6"
|
||||
version = "2.2.7"
|
||||
edition = "2018"
|
||||
authors = [
|
||||
"Sunshine <sunshine@uberspace.net>",
|
||||
|
||||
@@ -55,9 +55,9 @@ The guide can be found [here](docs/containers.md)
|
||||
- `-j`: Exclude JavaScript
|
||||
- `-k`: Accept invalid X.509 (TLS) certificates
|
||||
- `-o`: Write output to file
|
||||
- `-s`: Silent mode
|
||||
- `-t`: Set custom network request timeout
|
||||
- `-u`: Provide own User-Agent
|
||||
- `-s`: Enable silent mode
|
||||
- `-t`: Adjust network request timeout
|
||||
- `-u`: Provide custom User-Agent
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ Accepted
|
||||
|
||||
## Context
|
||||
|
||||
HTML pages sometimes contain NOSCRIPT nodes, which reveal their contents only in case when JavaScript is not available. Most of the time they contain hidden messages that inform about certain JavaScript-dependent features not being operational, however sometimes can also feature media assets or even iframes.
|
||||
HTML pages can contain `noscript` nodes, which reveal their contents only in case when JavaScript is not available. Most of the time they contain hidden messages that inform about certain JavaScript-dependent features not being operational, however sometimes can also feature media assets or even iframes.
|
||||
|
||||
## Decision
|
||||
|
||||
When the document is being saved with or without JavaScript, each NOSCRIPT node should be preserved while its children need to be processed exactly the same way as the rest of the document. This approach will ensure that even hidden remote assets are embedded — since those hidden elements may have to be displayed later in a browser that has JavaScript turned off. An option should be available to "unwrap" all NOSCRIPT nodes in order to make their contents always visible in the document, complimenting the "disable JS" function of the program.
|
||||
When the document is being saved with or without JavaScript, each `noscript` node should be preserved while its children need to be processed exactly the same way as the rest of the document. This approach will ensure that even hidden remote assets are embedded — since those hidden elements may have to be displayed later in a browser that has JavaScript turned off. An option should be available to "unwrap" all `noscript` nodes in order to make their contents always visible in the document, complimenting the "disable JS" function of the program.
|
||||
|
||||
## Consequences
|
||||
|
||||
Saved documents will have contents of all NOSCRIPT nodes processed as if they are part of the document's DOM, therefore properly display images encapsulated within NOSCRIPT nodes when being viewed in browsers that have JavaScript turned off (or have no JavaScript support in the first place). The new option to "unwrap" NOSCRIPT elements will help the user ensure that the resulting document always represents what the original web page looked like in a browser that had JavaScript turned off.
|
||||
Saved documents will have contents of all `noscript` nodes processed as if they are part of the document's DOM, therefore properly display images encapsulated within `noscript` nodes when being viewed in browsers that have JavaScript turned off (or have no JavaScript support in the first place). The new option to "unwrap" `noscript` elements will help the user ensure that the resulting document always represents what the original web page looked like in a browser that had JavaScript turned off.
|
||||
|
||||
@@ -12,14 +12,10 @@ A slow network connection and overloaded server may negatively impact network re
|
||||
|
||||
## Decision
|
||||
|
||||
Make the program simulate behavior of popular web browsers and CLI tools, where
|
||||
the default network response timeout is most often set to 120 seconds.
|
||||
Make the program simulate behavior of popular web browsers and CLI tools, where the default network response timeout is most often set to 120 seconds.
|
||||
|
||||
Instead of featuring retries for timed out network requests, the program
|
||||
should have an option to adjust the timeout length, along with making it
|
||||
indefinite when given "0" as its value.
|
||||
Instead of featuring retries for timed out network requests, the program should have an option to adjust the timeout length, along with making it indefinite when given "0" as its value.
|
||||
|
||||
## Consequences
|
||||
|
||||
The user is able to retrieve resources that have long response time, as well as obtain
|
||||
full control over how soon, and if at all, network requests should time out.
|
||||
The user is able to retrieve resources that have long response time, as well as obtain full control over how soon, and if at all, network requests should time out.
|
||||
|
||||
21
docs/arch/0004-asset-integrity-check.md
Normal file
21
docs/arch/0004-asset-integrity-check.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 4. Asset integrity check
|
||||
|
||||
Date: 2020-02-23
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
In HTML5, `link` and `script` nodes have an attribute named `integrity`, which lets the browser check if the remote file is valid, mostly for the purpose of enhancing page security.
|
||||
|
||||
## Decision
|
||||
|
||||
In order to replicate the browser's behavior, the program should perform integrity check the same way it does, excluding the linked asset from the final result if such check fails.
|
||||
|
||||
The `integrity` attribute should be removed from nodes, as it bears no benefit for resources embedded as data URLs.
|
||||
|
||||
## Consequences
|
||||
|
||||
Assets that fail to pass the check get excluded from the saved document. Meanwhile, saved documents no longer contain integrity attributes on all `link` and `script` nodes.
|
||||
@@ -8,18 +8,12 @@ Accepted
|
||||
|
||||
## Context
|
||||
|
||||
It may look like a good idea to make monolith compress retrieved assets while
|
||||
saving the page for the purpose of reducing the resulting document's file size.
|
||||
It may look like a good idea to make monolith compress retrieved assets while saving the page for the purpose of reducing the resulting document's file size.
|
||||
|
||||
## Decision
|
||||
|
||||
Given that the main purpose of this program is to save pages in a convenient to store and share manner — it's mostly an archiving tool,
|
||||
aside from being able to tell monolith to exclude certain types of asests (e.g. images, CSS, JavaScript),
|
||||
it would be outside of scope of this program to implement code for compressing assets. Minimizing files before embedding them
|
||||
does not reduce the amount of data that needs to be transferred either. A separate tool can be used later to compress and minimize pages
|
||||
saved by monolith, if needed.
|
||||
Given that the main purpose of this program is to save pages in a convenient to store and share manner — it's mostly an archiving tool, aside from being able to tell monolith to exclude certain types of asests (e.g. images, CSS, JavaScript), it would be outside of scope of this program to implement code for compressing assets. Minimizing files before embedding them does not reduce the amount of data that needs to be transferred either. A separate tool can be used later to compress and minimize pages saved by monolith, if needed.
|
||||
|
||||
## Consequences
|
||||
|
||||
Monolith will not support modification of original document assets for the purpose of reducing their size, sticking to performing only minimal
|
||||
amount of modifications to the original web page — whatever is needed to provide security or exclude unwanted asset types.
|
||||
Monolith will not support modification of original document assets for the purpose of reducing their size, sticking to performing only minimal amount of modifications to the original web page — whatever is needed to provide security or exclude unwanted asset types.
|
||||
|
||||
@@ -160,9 +160,12 @@ fn main() {
|
||||
// Safe to unwrap (we just put this through an HTTP request)
|
||||
let mut clean_url = Url::parse(&base_url).unwrap();
|
||||
clean_url.set_fragment(None);
|
||||
// Don't include credentials
|
||||
clean_url.set_username("").unwrap();
|
||||
clean_url.set_password(None).unwrap();
|
||||
// Prevent credentials from getting into metadata
|
||||
if is_http_url(&base_url) {
|
||||
// Only HTTP(S) URLs may feature credentials
|
||||
clean_url.set_username("").unwrap();
|
||||
clean_url.set_password(None).unwrap();
|
||||
}
|
||||
let metadata_comment = if is_http_url(&base_url) {
|
||||
format!(
|
||||
"<!-- Saved from {} at {} using {} v{} -->\n",
|
||||
|
||||
900
src/tests/cli.rs
900
src/tests/cli.rs
@@ -1,9 +1,3 @@
|
||||
use assert_cmd::prelude::*;
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -11,519 +5,527 @@ use tempfile::NamedTempFile;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_print_version() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("-V").output().unwrap();
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use assert_cmd::prelude::*;
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
// STDOUT should contain program name and version
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!("{} {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
|
||||
);
|
||||
#[test]
|
||||
fn print_version() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("-V").output().unwrap();
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDOUT should contain program name and version
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!("{} {}\n", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_bad_input_empty_target() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("").output().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stdout).unwrap(), "");
|
||||
#[test]
|
||||
fn bad_input_empty_target() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("").output().unwrap();
|
||||
|
||||
// STDERR should contain error description
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
"No target specified\n"
|
||||
);
|
||||
// STDOUT should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stdout).unwrap(), "");
|
||||
|
||||
// The exit code should be 1
|
||||
out.assert().code(1);
|
||||
// STDERR should contain error description
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
"No target specified\n"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 1
|
||||
out.assert().code(1);
|
||||
|
||||
#[test]
|
||||
fn passing_bad_input_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("data:,Hello%2C%20World!").output().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML
|
||||
assert_eq!(std::str::from_utf8(&out.stdout).unwrap(), "");
|
||||
#[test]
|
||||
fn bad_input_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd.arg("data:,Hello%2C%20World!").output().unwrap();
|
||||
|
||||
// STDERR should contain error description
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
"Unsupported data URL media type\n"
|
||||
);
|
||||
// STDOUT should contain HTML
|
||||
assert_eq!(std::str::from_utf8(&out.stdout).unwrap(), "");
|
||||
|
||||
// The exit code should be 1
|
||||
out.assert().code(1);
|
||||
// STDERR should contain error description
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
"Unsupported data URL media type\n"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 1
|
||||
out.assert().code(1);
|
||||
|
||||
#[test]
|
||||
fn passing_isolate_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-I")
|
||||
.arg("data:text/html,Hello%2C%20World!")
|
||||
.output()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain isolated HTML
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:;\"></meta>\
|
||||
</head><body>Hello, World!</body></html>\n"
|
||||
);
|
||||
#[test]
|
||||
fn isolate_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-I")
|
||||
.arg("data:text/html,Hello%2C%20World!")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDOUT should contain isolated HTML
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:;\"></meta>\
|
||||
</head><body>Hello, World!</body></html>\n"
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_remove_css_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-c")
|
||||
.arg("data:text/html,<style>body{background-color:pink}</style>Hello")
|
||||
.output()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML with no CSS
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none';\"></meta>\
|
||||
<style></style>\
|
||||
</head><body>Hello</body></html>\n"
|
||||
);
|
||||
#[test]
|
||||
fn remove_css_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-c")
|
||||
.arg("data:text/html,<style>body{background-color:pink}</style>Hello")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDOUT should contain HTML with no CSS
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none';\"></meta>\
|
||||
<style></style>\
|
||||
</head><body>Hello</body></html>\n"
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_remove_frames_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-f")
|
||||
.arg("data:text/html,<iframe src=\"https://google.com\"></iframe>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML with no iframes
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"frame-src 'none';child-src 'none';\"></meta>\
|
||||
</head><body><iframe src=\"\"></iframe>Hi</body></html>\n"
|
||||
);
|
||||
#[test]
|
||||
fn remove_frames_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-f")
|
||||
.arg("data:text/html,<iframe src=\"https://google.com\"></iframe>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDOUT should contain HTML with no iframes
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"frame-src 'none';child-src 'none';\"></meta>\
|
||||
</head><body><iframe src=\"\"></iframe>Hi</body></html>\n"
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_remove_images_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-i")
|
||||
.arg("data:text/html,<img src=\"https://google.com\"/>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML with no images
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
#[test]
|
||||
fn remove_images_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-i")
|
||||
.arg("data:text/html,<img src=\"https://google.com\"/>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDOUT should contain HTML with no images
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"img-src data:;\"></meta>\
|
||||
</head>\
|
||||
<body>\
|
||||
<img src=\"{empty_image}\">\
|
||||
Hi\
|
||||
</body>\
|
||||
</html>\n",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_js_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-j")
|
||||
.arg("data:text/html,<script>alert(2)</script>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDOUT should contain HTML with no JS
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"img-src data:;\"></meta>\
|
||||
</head>\
|
||||
<body>\
|
||||
<img src=\"{empty_image}\">\
|
||||
Hi\
|
||||
</body>\
|
||||
</html>\n",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'none';\"></meta>\
|
||||
<script></script></head>\
|
||||
<body>Hi</body>\
|
||||
</html>\n"
|
||||
);
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_remove_js_from_data_url() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-j")
|
||||
.arg("data:text/html,<script>alert(2)</script>Hi")
|
||||
.output()
|
||||
.unwrap();
|
||||
#[test]
|
||||
fn local_file_target_input() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg(if cfg!(windows) {
|
||||
"src\\tests\\data\\basic\\local-file.html"
|
||||
} else {
|
||||
"src/tests/data/basic/local-file.html"
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// STDOUT should contain HTML with no JS
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'none';\"></meta>\
|
||||
<script></script></head>\
|
||||
<body>Hi</body>\
|
||||
</html>\n"
|
||||
);
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_local_file_target_input() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg(if cfg!(windows) {
|
||||
"src\\tests\\data\\basic\\local-file.html"
|
||||
} else {
|
||||
"src/tests/data/basic/local-file.html"
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"\
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"data:text/css;base64,Ym9keSB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogICAgY29sb3I6ICNmZmY7Cn0K\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script src=\"data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==\"></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
"
|
||||
);
|
||||
|
||||
// STDERR should contain list of retrieved file URLs
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"\
|
||||
{file}{cwd}/src/tests/data/basic/local-file.html\n\
|
||||
{file}{cwd}/src/tests/data/basic/local-style.css\n\
|
||||
{file}{cwd}/src/tests/data/basic/local-script.js\n\
|
||||
",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized
|
||||
)
|
||||
);
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\" href=\"data:text/css;base64,Ym9keSB7CiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwOwogICAgY29sb3I6ICNmZmY7Cn0K\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script src=\"data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==\"></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
"
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_local_file_target_input_absolute_target_path() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-jciI")
|
||||
.arg(if cfg!(windows) {
|
||||
// STDERR should contain list of retrieved file URLs
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"{cwd}\\src\\tests\\data\\basic\\local-file.html",
|
||||
cwd = cwd.to_str().unwrap()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{cwd}/src/tests/data/basic/local-file.html",
|
||||
cwd = cwd.to_str().unwrap()
|
||||
)
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
"\
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:; style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\" src=\"{empty_image}\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
|
||||
// STDERR should contain only the target file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html\n",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_local_file_url_target_input() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-cji")
|
||||
.arg(if cfg!(windows) {
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
"\
|
||||
{file}{cwd}/src/tests/data/basic/local-file.html\n\
|
||||
{file}{cwd}/src/tests/data/basic/local-style.css\n\
|
||||
{file}{cwd}/src/tests/data/basic/local-script.js\n\
|
||||
",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
cwd = cwd_normalized
|
||||
)
|
||||
} else {
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_file_target_input_absolute_target_path() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-jciI")
|
||||
.arg(if cfg!(windows) {
|
||||
format!(
|
||||
"{cwd}\\src\\tests\\data\\basic\\local-file.html",
|
||||
cwd = cwd.to_str().unwrap()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{cwd}/src/tests/data/basic/local-file.html",
|
||||
cwd = cwd.to_str().unwrap()
|
||||
)
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
"\
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:; style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\" src=\"{empty_image}\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
);
|
||||
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
"\
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\" src=\"{empty_image}\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
|
||||
// STDERR should contain list of retrieved file URLs
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
if cfg!(windows) {
|
||||
// STDERR should contain only the target file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html\n",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
} else {
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_file_url_target_input() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let cwd_normalized: String =
|
||||
str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/");
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("-cji")
|
||||
.arg(if cfg!(windows) {
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
})
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDOUT should contain HTML from the local file
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html\n",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
"\
|
||||
<!DOCTYPE html><html lang=\"en\"><head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none'; script-src 'none'; img-src data:;\"></meta>\n \
|
||||
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n \
|
||||
<title>Local HTML file</title>\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n \
|
||||
<link rel=\"stylesheet\" type=\"text/css\">\n</head>\n\n<body>\n \
|
||||
<img alt=\"\" src=\"{empty_image}\">\n \
|
||||
<a href=\"file://local-file.html/\">Tricky href</a>\n \
|
||||
<a href=\"https://github.com/Y2Z/monolith\">Remote URL</a>\n \
|
||||
<script></script>\n\n\n\n\
|
||||
</body></html>\n\
|
||||
",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should contain list of retrieved file URLs
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
if cfg!(windows) {
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html\n",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html\n",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd_normalized,
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_security_disallow_local_assets_within_data_url_targets(
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("data:text/html,%3Cscript%20src=\"src/tests/data/basic/local-script.js\"%3E%3C/script%3E")
|
||||
.output()
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML with no JS in it
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head><script></script></head><body></body></html>\n"
|
||||
);
|
||||
#[test]
|
||||
fn security_disallow_local_assets_within_data_url_targets(
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let out = cmd
|
||||
.arg("-M")
|
||||
.arg("data:text/html,%3Cscript%20src=\"src/tests/data/basic/local-script.js\"%3E%3C/script%3E")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
// STDOUT should contain HTML with no JS in it
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head><script></script></head><body></body></html>\n"
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDERR should be empty
|
||||
assert_eq!(std::str::from_utf8(&out.stderr).unwrap(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
#[test]
|
||||
fn passing_embed_file_url_local_asset_within_style_attribute(
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_url_prefix: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let mut file_svg = NamedTempFile::new()?;
|
||||
writeln!(file_svg, "<svg version=\"1.1\" baseProfile=\"full\" width=\"300\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\">\
|
||||
<rect width=\"100%\" height=\"100%\" fill=\"red\" />\
|
||||
<circle cx=\"150\" cy=\"100\" r=\"80\" fill=\"green\" />\
|
||||
<text x=\"150\" y=\"125\" font-size=\"60\" text-anchor=\"middle\" fill=\"white\">SVG</text>\
|
||||
</svg>\n")?;
|
||||
let mut file_html = NamedTempFile::new()?;
|
||||
writeln!(
|
||||
file_html,
|
||||
"<div style='background-image: url(\"{file}{path}\")'></div>\n",
|
||||
file = file_url_prefix,
|
||||
path = str!(file_svg.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)?;
|
||||
let out = cmd.arg("-M").arg(file_html.path()).output().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDOUT should contain HTML with date URL for background-image in it
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head></head><body><div style=\"background-image: url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJmdWxsIiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJyZWQiIC8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMTAwIiByPSI4MCIgZmlsbD0iZ3JlZW4iIC8+PHRleHQgeD0iMTUwIiB5PSIxMjUiIGZvbnQtc2l6ZT0iNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj5TVkc8L3RleHQ+PC9zdmc+Cgo=')\"></div>\n\n</body></html>\n"
|
||||
);
|
||||
|
||||
// STDERR should list temporary files that got retrieved
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"\
|
||||
{file}{html_path}\n\
|
||||
{file}{svg_path}\n\
|
||||
",
|
||||
#[test]
|
||||
fn embed_file_url_local_asset_within_style_attribute() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let file_url_prefix: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let mut file_svg = NamedTempFile::new()?;
|
||||
writeln!(file_svg, "<svg version=\"1.1\" baseProfile=\"full\" width=\"300\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\">\
|
||||
<rect width=\"100%\" height=\"100%\" fill=\"red\" />\
|
||||
<circle cx=\"150\" cy=\"100\" r=\"80\" fill=\"green\" />\
|
||||
<text x=\"150\" y=\"125\" font-size=\"60\" text-anchor=\"middle\" fill=\"white\">SVG</text>\
|
||||
</svg>\n")?;
|
||||
let mut file_html = NamedTempFile::new()?;
|
||||
writeln!(
|
||||
file_html,
|
||||
"<div style='background-image: url(\"{file}{path}\")'></div>\n",
|
||||
file = file_url_prefix,
|
||||
html_path = str!(file_html.path().to_str().unwrap()).replace("\\", "/"),
|
||||
svg_path = str!(file_svg.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)
|
||||
);
|
||||
path = str!(file_svg.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)?;
|
||||
let out = cmd.arg("-M").arg(file_html.path()).output().unwrap();
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDOUT should contain HTML with date URL for background-image in it
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head></head><body><div style=\"background-image: url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGJhc2VQcm9maWxlPSJmdWxsIiB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJyZWQiIC8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iMTAwIiByPSI4MCIgZmlsbD0iZ3JlZW4iIC8+PHRleHQgeD0iMTUwIiB5PSIxMjUiIGZvbnQtc2l6ZT0iNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IndoaXRlIj5TVkc8L3RleHQ+PC9zdmc+Cgo=')\"></div>\n\n</body></html>\n"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// STDERR should list temporary files that got retrieved
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"\
|
||||
{file}{html_path}\n\
|
||||
{file}{svg_path}\n\
|
||||
",
|
||||
file = file_url_prefix,
|
||||
html_path = str!(file_html.path().to_str().unwrap()).replace("\\", "/"),
|
||||
svg_path = str!(file_svg.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_css_import_string() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_url_prefix: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let mut file_css = NamedTempFile::new()?;
|
||||
writeln!(file_css, "body{{background-color:#000;color:#fff}}")?;
|
||||
let mut file_html = NamedTempFile::new()?;
|
||||
writeln!(
|
||||
file_html,
|
||||
"\
|
||||
<style>\n\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import '{file}{css_path}';\n\
|
||||
\n\
|
||||
@import url({file}{css_path});\n\
|
||||
\n\
|
||||
@import url('{file}{css_path}')\n\
|
||||
</style>\n\
|
||||
",
|
||||
file = file_url_prefix,
|
||||
css_path = str!(file_css.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)?;
|
||||
let out = cmd.arg("-M").arg(file_html.path()).output().unwrap();
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
// STDOUT should contain embedded CSS url()'s
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head><style>\n@charset 'UTF-8';\n\n@import 'data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K';\n\n@import url('data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K');\n\n@import url('data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K')\n</style>\n\n</head><body></body></html>\n"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// STDERR should list temporary files that got retrieved
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
#[test]
|
||||
fn css_import_string() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_url_prefix: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?;
|
||||
let mut file_css = NamedTempFile::new()?;
|
||||
writeln!(file_css, "body{{background-color:#000;color:#fff}}")?;
|
||||
let mut file_html = NamedTempFile::new()?;
|
||||
writeln!(
|
||||
file_html,
|
||||
"\
|
||||
{file}{html_path}\n\
|
||||
{file}{css_path}\n\
|
||||
{file}{css_path}\n\
|
||||
{file}{css_path}\n\
|
||||
",
|
||||
<style>\n\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import '{file}{css_path}';\n\
|
||||
\n\
|
||||
@import url({file}{css_path});\n\
|
||||
\n\
|
||||
@import url('{file}{css_path}')\n\
|
||||
</style>\n\
|
||||
",
|
||||
file = file_url_prefix,
|
||||
html_path = str!(file_html.path().to_str().unwrap()).replace("\\", "/"),
|
||||
css_path = str!(file_css.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)
|
||||
);
|
||||
)?;
|
||||
let out = cmd.arg("-M").arg(file_html.path()).output().unwrap();
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
// STDOUT should contain embedded CSS url()'s
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stdout).unwrap(),
|
||||
"<html><head><style>\n@charset 'UTF-8';\n\n@import 'data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K';\n\n@import url('data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K');\n\n@import url('data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kLWNvbG9yOiMwMDA7Y29sb3I6I2ZmZn0K')\n</style>\n\n</head><body></body></html>\n"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
// STDERR should list temporary files that got retrieved
|
||||
assert_eq!(
|
||||
std::str::from_utf8(&out.stderr).unwrap(),
|
||||
format!(
|
||||
"\
|
||||
{file}{html_path}\n\
|
||||
{file}{css_path}\n\
|
||||
{file}{css_path}\n\
|
||||
{file}{css_path}\n\
|
||||
",
|
||||
file = file_url_prefix,
|
||||
html_path = str!(file_html.path().to_str().unwrap()).replace("\\", "/"),
|
||||
css_path = str!(file_css.path().to_str().unwrap()).replace("\\", "/"),
|
||||
)
|
||||
);
|
||||
|
||||
// The exit code should be 0
|
||||
out.assert().code(0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::css;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -10,308 +5,315 @@ use crate::css;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_empty_input() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::css;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", "", false, false, false,),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_style_exclude_unquoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url(https://somewhere.com/bg.png); \
|
||||
list-style: url(/assets/images/bullet.svg);\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&STYLE,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
background-image: url('{empty_image}'); \
|
||||
list-style: url('{empty_image}');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_style_exclude_single_quoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url('https://somewhere.com/bg.png'); \
|
||||
list-style: url('/assets/images/bullet.svg');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &STYLE, false, true, true,),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
background-image: url('{empty_image}'); \
|
||||
list-style: url('{empty_image}');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_style_block() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
#id.class-name:not(:nth-child(3n+0)) {\n \
|
||||
// border: none;\n \
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');\n\
|
||||
}\n\
|
||||
\n\
|
||||
html > body {}";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "file:///", &CSS, false, false, true,),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_attribute_selectors() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
[data-value] {
|
||||
/* Attribute exists */
|
||||
}
|
||||
|
||||
[data-value='foo'] {
|
||||
/* Attribute has this exact value */
|
||||
}
|
||||
|
||||
[data-value*='foo'] {
|
||||
/* Attribute value contains this value somewhere in it */
|
||||
}
|
||||
|
||||
[data-value~='foo'] {
|
||||
/* Attribute has this value in a space-separated list somewhere */
|
||||
}
|
||||
|
||||
[data-value^='foo'] {
|
||||
/* Attribute value starts with this */
|
||||
}
|
||||
|
||||
[data-value|='foo'] {
|
||||
/* Attribute value starts with this in a dash-separated list */
|
||||
}
|
||||
|
||||
[data-value$='foo'] {
|
||||
/* Attribute value ends with this */
|
||||
}
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &CSS, false, false, false,),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_import_string() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import 'data:text/css,html{background-color:%23000}';\n\
|
||||
\n\
|
||||
@import url('data:text/css,html{color:%23fff}')\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
"\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import 'data:text/css;base64,aHRtbHtiYWNrZ3JvdW5kLWNvbG9yOiMwMDB9';\n\
|
||||
\n\
|
||||
@import url('data:text/css;base64,aHRtbHtjb2xvcjojZmZmfQ==')\n\
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_hash_urls() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
body {\n \
|
||||
behavior: url(#default#something);\n\
|
||||
}\n\
|
||||
\n\
|
||||
.scissorHalf {\n \
|
||||
offset-path: url(#somePath);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_transform_percentages_and_degrees() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
div {\n \
|
||||
transform: translate(-50%, -50%) rotate(-45deg);\n\
|
||||
transform: translate(50%, 50%) rotate(45deg);\n\
|
||||
transform: translate(+50%, +50%) rotate(+45deg);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_unusual_indents() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
.is\\:good:hover {\n \
|
||||
color: green\n\
|
||||
}\n\
|
||||
\n\
|
||||
#\\~\\!\\@\\$\\%\\^\\&\\*\\(\\)\\+\\=\\,\\.\\/\\\\\\'\\\"\\;\\:\\?\\>\\<\\[\\]\\{\\}\\|\\`\\# {\n \
|
||||
color: black\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_exclude_fonts() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
@font-face {\n \
|
||||
font-family: 'My Font';\n \
|
||||
src: url(my_font.woff);\n\
|
||||
}\n\
|
||||
\n\
|
||||
#identifier {\n \
|
||||
font-family: 'My Font' Arial\n\
|
||||
}\n\
|
||||
\n\
|
||||
@font-face {\n \
|
||||
font-family: 'My Font';\n \
|
||||
src: url(my_font.woff);\n\
|
||||
}\n\
|
||||
\n\
|
||||
div {\n \
|
||||
font-family: 'My Font' Verdana\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
const CSS_OUT: &str = " \
|
||||
\n\
|
||||
\n\
|
||||
#identifier {\n \
|
||||
font-family: 'My Font' Arial\n\
|
||||
}\n\
|
||||
\n \
|
||||
\n\
|
||||
\n\
|
||||
div {\n \
|
||||
font-family: 'My Font' Verdana\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS_OUT
|
||||
);
|
||||
#[test]
|
||||
fn empty_input() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", "", false, false, false,),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_exclude_unquoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url(https://somewhere.com/bg.png); \
|
||||
list-style: url(/assets/images/bullet.svg);\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&STYLE,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
background-image: url('{empty_image}'); \
|
||||
list-style: url('{empty_image}');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_exclude_single_quoted_images() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const STYLE: &str = "/* border: none;*/\
|
||||
background-image: url('https://somewhere.com/bg.png'); \
|
||||
list-style: url('/assets/images/bullet.svg');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &STYLE, false, true, true,),
|
||||
format!(
|
||||
"/* border: none;*/\
|
||||
background-image: url('{empty_image}'); \
|
||||
list-style: url('{empty_image}');\
|
||||
width:99.998%; \
|
||||
margin-top: -20px; \
|
||||
line-height: -1; \
|
||||
height: calc(100vh - 10pt)",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_block() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
#id.class-name:not(:nth-child(3n+0)) {\n \
|
||||
// border: none;\n \
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=');\n\
|
||||
}\n\
|
||||
\n\
|
||||
html > body {}";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "file:///", &CSS, false, false, true,),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attribute_selectors() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
[data-value] {
|
||||
/* Attribute exists */
|
||||
}
|
||||
|
||||
[data-value='foo'] {
|
||||
/* Attribute has this exact value */
|
||||
}
|
||||
|
||||
[data-value*='foo'] {
|
||||
/* Attribute value contains this value somewhere in it */
|
||||
}
|
||||
|
||||
[data-value~='foo'] {
|
||||
/* Attribute has this value in a space-separated list somewhere */
|
||||
}
|
||||
|
||||
[data-value^='foo'] {
|
||||
/* Attribute value starts with this */
|
||||
}
|
||||
|
||||
[data-value|='foo'] {
|
||||
/* Attribute value starts with this in a dash-separated list */
|
||||
}
|
||||
|
||||
[data-value$='foo'] {
|
||||
/* Attribute value ends with this */
|
||||
}
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(cache, &client, "", &CSS, false, false, false,),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_string() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import 'data:text/css,html{background-color:%23000}';\n\
|
||||
\n\
|
||||
@import url('data:text/css,html{color:%23fff}')\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
"\
|
||||
@charset 'UTF-8';\n\
|
||||
\n\
|
||||
@import 'data:text/css;base64,aHRtbHtiYWNrZ3JvdW5kLWNvbG9yOiMwMDB9';\n\
|
||||
\n\
|
||||
@import url('data:text/css;base64,aHRtbHtjb2xvcjojZmZmfQ==')\n\
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_urls() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
body {\n \
|
||||
behavior: url(#default#something);\n\
|
||||
}\n\
|
||||
\n\
|
||||
.scissorHalf {\n \
|
||||
offset-path: url(#somePath);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transform_percentages_and_degrees() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
div {\n \
|
||||
transform: translate(-50%, -50%) rotate(-45deg);\n\
|
||||
transform: translate(50%, 50%) rotate(45deg);\n\
|
||||
transform: translate(+50%, +50%) rotate(+45deg);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unusual_indents() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
.is\\:good:hover {\n \
|
||||
color: green\n\
|
||||
}\n\
|
||||
\n\
|
||||
#\\~\\!\\@\\$\\%\\^\\&\\*\\(\\)\\+\\=\\,\\.\\/\\\\\\'\\\"\\;\\:\\?\\>\\<\\[\\]\\{\\}\\|\\`\\# {\n \
|
||||
color: black\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exclude_fonts() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
const CSS: &str = "\
|
||||
@font-face {\n \
|
||||
font-family: 'My Font';\n \
|
||||
src: url(my_font.woff);\n\
|
||||
}\n\
|
||||
\n\
|
||||
#identifier {\n \
|
||||
font-family: 'My Font' Arial\n\
|
||||
}\n\
|
||||
\n\
|
||||
@font-face {\n \
|
||||
font-family: 'My Font';\n \
|
||||
src: url(my_font.woff);\n\
|
||||
}\n\
|
||||
\n\
|
||||
div {\n \
|
||||
font-family: 'My Font' Verdana\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
const CSS_OUT: &str = " \
|
||||
\n\
|
||||
\n\
|
||||
#identifier {\n \
|
||||
font-family: 'My Font' Arial\n\
|
||||
}\n\
|
||||
\n \
|
||||
\n\
|
||||
\n\
|
||||
div {\n \
|
||||
font-family: 'My Font' Verdana\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
css::embed_css(
|
||||
cache,
|
||||
&client,
|
||||
"https://doesntmatter.local/",
|
||||
&CSS,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
),
|
||||
CSS_OUT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::css;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,44 +5,49 @@ use crate::css;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_empty_input_single_quotes() {
|
||||
assert_eq!(css::enquote(str!(""), false), "''");
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::css;
|
||||
|
||||
#[test]
|
||||
fn passing_empty_input_double_quotes() {
|
||||
assert_eq!(css::enquote(str!(""), true), "\"\"");
|
||||
}
|
||||
#[test]
|
||||
fn empty_input_single_quotes() {
|
||||
assert_eq!(css::enquote(str!(""), false), "''");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_apostrophes_single_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("It's a lovely day, don't you think?"), false),
|
||||
"'It\\'s a lovely day, don\\'t you think?'"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn empty_input_double_quotes() {
|
||||
assert_eq!(css::enquote(str!(""), true), "\"\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_apostrophes_double_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("It's a lovely day, don't you think?"), true),
|
||||
"\"It's a lovely day, don't you think?\""
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn apostrophes_single_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("It's a lovely day, don't you think?"), false),
|
||||
"'It\\'s a lovely day, don\\'t you think?'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_feet_and_inches_single_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("5'2\", 6'5\""), false),
|
||||
"'5\\'2\", 6\\'5\"'"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn apostrophes_double_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("It's a lovely day, don't you think?"), true),
|
||||
"\"It's a lovely day, don't you think?\""
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_feet_and_inches_double_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("5'2\", 6'5\""), true),
|
||||
"\"5'2\\\", 6'5\\\"\""
|
||||
);
|
||||
#[test]
|
||||
fn feet_and_inches_single_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("5'2\", 6'5\""), false),
|
||||
"'5\\'2\", 6\\'5\"'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn feet_and_inches_double_quotes() {
|
||||
assert_eq!(
|
||||
css::enquote(str!("5'2\", 6'5\""), true),
|
||||
"\"5'2\\\", 6'5\\\"\""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
use crate::html;
|
||||
use html5ever::rcdom::{Handle, NodeData};
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -8,42 +5,48 @@ use html5ever::rcdom::{Handle, NodeData};
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn get_node_name() {
|
||||
let html = "<!doctype html><html><HEAD></HEAD><body><div><P></P></div></body></html>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let mut count = 0;
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use html5ever::rcdom::{Handle, NodeData};
|
||||
|
||||
fn test_walk(node: &Handle, i: &mut i8) {
|
||||
*i += 1;
|
||||
#[test]
|
||||
fn get_node_name() {
|
||||
let html = "<!doctype html><html><HEAD></HEAD><body><div><P></P></div></body></html>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let mut count = 0;
|
||||
|
||||
match &node.data {
|
||||
NodeData::Document => {
|
||||
for child in node.children.borrow().iter() {
|
||||
test_walk(child, &mut *i);
|
||||
}
|
||||
}
|
||||
NodeData::Element { ref name, .. } => {
|
||||
let node_name = name.local.as_ref().to_string();
|
||||
let parent = html::get_parent_node(node);
|
||||
let parent_node_name = html::get_node_name(&parent);
|
||||
if node_name == "head" || node_name == "body" {
|
||||
assert_eq!(parent_node_name, Some("html"));
|
||||
} else if node_name == "div" {
|
||||
assert_eq!(parent_node_name, Some("body"));
|
||||
} else if node_name == "p" {
|
||||
assert_eq!(parent_node_name, Some("div"));
|
||||
}
|
||||
fn test_walk(node: &Handle, i: &mut i8) {
|
||||
*i += 1;
|
||||
|
||||
for child in node.children.borrow().iter() {
|
||||
test_walk(child, &mut *i);
|
||||
match &node.data {
|
||||
NodeData::Document => {
|
||||
for child in node.children.borrow().iter() {
|
||||
test_walk(child, &mut *i);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
NodeData::Element { ref name, .. } => {
|
||||
let node_name = name.local.as_ref().to_string();
|
||||
let parent = html::get_parent_node(node);
|
||||
let parent_node_name = html::get_node_name(&parent);
|
||||
if node_name == "head" || node_name == "body" {
|
||||
assert_eq!(parent_node_name, Some("html"));
|
||||
} else if node_name == "div" {
|
||||
assert_eq!(parent_node_name, Some("body"));
|
||||
} else if node_name == "p" {
|
||||
assert_eq!(parent_node_name, Some("div"));
|
||||
}
|
||||
|
||||
for child in node.children.borrow().iter() {
|
||||
test_walk(child, &mut *i);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
test_walk(&dom.document, &mut count);
|
||||
|
||||
assert_eq!(count, 7);
|
||||
}
|
||||
|
||||
test_walk(&dom.document, &mut count);
|
||||
|
||||
assert_eq!(count, 7);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::html;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,29 +5,34 @@ use crate::html;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_icon() {
|
||||
assert!(html::is_icon("icon"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
|
||||
#[test]
|
||||
fn passing_shortcut_icon_capitalized() {
|
||||
assert!(html::is_icon("Shortcut Icon"));
|
||||
}
|
||||
#[test]
|
||||
fn icon() {
|
||||
assert!(html::is_icon("icon"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_icon_uppercase() {
|
||||
assert!(html::is_icon("ICON"));
|
||||
}
|
||||
#[test]
|
||||
fn shortcut_icon_capitalized() {
|
||||
assert!(html::is_icon("Shortcut Icon"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_mask_icon() {
|
||||
assert!(html::is_icon("mask-icon"));
|
||||
}
|
||||
#[test]
|
||||
fn icon_uppercase() {
|
||||
assert!(html::is_icon("ICON"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_fluid_icon() {
|
||||
assert!(html::is_icon("fluid-icon"));
|
||||
#[test]
|
||||
fn mask_icon() {
|
||||
assert!(html::is_icon("mask-icon"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fluid_icon() {
|
||||
assert!(html::is_icon("fluid-icon"));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -39,12 +42,17 @@ fn passing_fluid_icon() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_stylesheet() {
|
||||
assert!(!html::is_icon("stylesheet"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::html;
|
||||
|
||||
#[test]
|
||||
fn failing_empty_string() {
|
||||
assert!(!html::is_icon(""));
|
||||
#[test]
|
||||
fn stylesheet() {
|
||||
assert!(!html::is_icon("stylesheet"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert!(!html::is_icon(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::html;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,182 +5,187 @@ use crate::html;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_div_as_root_element() {
|
||||
let html = "<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
#[test]
|
||||
fn div_as_root_element() {
|
||||
let html = "<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<html><head></head><body><div><script src=\"some.js\"></script></div></body></html>"
|
||||
);
|
||||
}
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
|
||||
#[test]
|
||||
fn passing_full_page_with_no_html_head_or_body() {
|
||||
let html = "<title>Isolated document</title>\
|
||||
<link rel=\"something\" href=\"some.css\" />\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<html><head></head><body><div><script src=\"some.js\"></script></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:;\"></meta>\
|
||||
<title>Isolated document</title>\
|
||||
<link rel=\"something\" href=\"some.css\">\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<div>\
|
||||
<script src=\"some.js\"></script>\
|
||||
</div>\
|
||||
</body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_doctype_and_the_rest_no_html_head_or_body() {
|
||||
let html = "<!doctype html>\
|
||||
<title>Unstyled document</title>\
|
||||
<link rel=\"stylesheet\" href=\"main.css\"/>\
|
||||
<div style=\"display: none;\"></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none';\"></meta>\
|
||||
<title>Unstyled document</title>\
|
||||
<link rel=\"stylesheet\" href=\"main.css\">\
|
||||
</head>\
|
||||
<body><div style=\"display: none;\"></div></body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_doctype_and_the_rest_no_html_head_or_body_forbid_frames() {
|
||||
let html = "<!doctype html>\
|
||||
<title>Frameless document</title>\
|
||||
<link rel=\"something\"/>\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"frame-src 'none';child-src 'none';\"></meta>\
|
||||
<title>Frameless document</title>\
|
||||
<link rel=\"something\">\
|
||||
</head>\
|
||||
<body><div><script src=\"some.js\"></script></div></body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_doctype_and_the_rest_all_forbidden() {
|
||||
let html = "<!doctype html>\
|
||||
<title>no-frame no-css no-js no-image isolated document</title>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<link rel=\"stylesheet\" href=\"some.css\">\
|
||||
<div>\
|
||||
<script src=\"some.js\"></script>\
|
||||
<img style=\"width: 100%;\" src=\"some.png\" />\
|
||||
<iframe src=\"some.html\"></iframe>\
|
||||
</div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_isolate: bool = true;
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:; style-src 'none'; frame-src 'none';child-src 'none'; script-src 'none'; img-src data:;\"></meta>\
|
||||
<title>no-frame no-css no-js no-image isolated document</title>\
|
||||
#[test]
|
||||
fn full_page_with_no_html_head_or_body() {
|
||||
let html = "<title>Isolated document</title>\
|
||||
<link rel=\"something\" href=\"some.css\" />\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:;\"></meta>\
|
||||
<title>Isolated document</title>\
|
||||
<link rel=\"something\" href=\"some.css\">\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<link rel=\"stylesheet\" href=\"some.css\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<div>\
|
||||
<script src=\"some.js\"></script>\
|
||||
<img style=\"width: 100%;\" src=\"some.png\">\
|
||||
<iframe src=\"some.html\"></iframe>\
|
||||
</div>\
|
||||
</body>\
|
||||
</html>"
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctype_and_the_rest_no_html_head_or_body() {
|
||||
let html = "<!doctype html>\
|
||||
<title>Unstyled document</title>\
|
||||
<link rel=\"stylesheet\" href=\"main.css\"/>\
|
||||
<div style=\"display: none;\"></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"style-src 'none';\"></meta>\
|
||||
<title>Unstyled document</title>\
|
||||
<link rel=\"stylesheet\" href=\"main.css\">\
|
||||
</head>\
|
||||
<body><div style=\"display: none;\"></div></body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctype_and_the_rest_no_html_head_or_body_forbid_frames() {
|
||||
let html = "<!doctype html>\
|
||||
<title>Frameless document</title>\
|
||||
<link rel=\"something\"/>\
|
||||
<div><script src=\"some.js\"></script></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_isolate: bool = false;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"frame-src 'none';child-src 'none';\"></meta>\
|
||||
<title>Frameless document</title>\
|
||||
<link rel=\"something\">\
|
||||
</head>\
|
||||
<body><div><script src=\"some.js\"></script></div></body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctype_and_the_rest_all_forbidden() {
|
||||
let html = "<!doctype html>\
|
||||
<title>no-frame no-css no-js no-image isolated document</title>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<link rel=\"stylesheet\" href=\"some.css\">\
|
||||
<div>\
|
||||
<script src=\"some.js\"></script>\
|
||||
<img style=\"width: 100%;\" src=\"some.png\" />\
|
||||
<iframe src=\"some.html\"></iframe>\
|
||||
</div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
|
||||
let opt_isolate: bool = true;
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
|
||||
assert_eq!(
|
||||
html::stringify_document(
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_isolate,
|
||||
),
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'unsafe-inline' data:; style-src 'none'; frame-src 'none';child-src 'none'; script-src 'none'; img-src data:;\"></meta>\
|
||||
<title>no-frame no-css no-js no-image isolated document</title>\
|
||||
<meta http-equiv=\"Content-Security-Policy\" content=\"default-src https:\">\
|
||||
<link rel=\"stylesheet\" href=\"some.css\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<div>\
|
||||
<script src=\"some.js\"></script>\
|
||||
<img style=\"width: 100%;\" src=\"some.png\">\
|
||||
<iframe src=\"some.html\"></iframe>\
|
||||
</div>\
|
||||
</body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
use crate::html;
|
||||
use html5ever::serialize::{serialize, SerializeOpts};
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -10,410 +5,419 @@ use std::collections::HashMap;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_basic() {
|
||||
let cache = &mut HashMap::new();
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::html;
|
||||
use html5ever::serialize::{serialize, SerializeOpts};
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
let html = "<div><P></P></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
#[test]
|
||||
fn basic() {
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let html = "<div><P></P></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let client = Client::new();
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><p></p></div></body></html>"
|
||||
);
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn passing_ensure_no_recursive_iframe() {
|
||||
let html = "<div><P></P><iframe src=\"\"></iframe></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><p></p></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
#[test]
|
||||
fn ensure_no_recursive_iframe() {
|
||||
let html = "<div><P></P><iframe src=\"\"></iframe></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let client = Client::new();
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><p></p><iframe src=\"\"></iframe></div></body></html>"
|
||||
);
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn passing_ensure_no_recursive_frame() {
|
||||
let html = "<frameset><frame src=\"\"></frameset>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><p></p><iframe src=\"\"></iframe></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
#[test]
|
||||
fn ensure_no_recursive_frame() {
|
||||
let html = "<frameset><frame src=\"\"></frameset>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let client = Client::new();
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><frameset><frame src=\"\"></frameset></html>"
|
||||
);
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn passing_no_css() {
|
||||
let html = "<link rel=\"stylesheet\" href=\"main.css\">\
|
||||
<style>html{background-color: #000;}</style>\
|
||||
<div style=\"display: none;\"></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><frameset><frame src=\"\"></frameset></html>"
|
||||
);
|
||||
}
|
||||
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
#[test]
|
||||
fn no_css() {
|
||||
let html = "<link rel=\"stylesheet\" href=\"main.css\">\
|
||||
<style>html{background-color: #000;}</style>\
|
||||
<div style=\"display: none;\"></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html>\
|
||||
<head>\
|
||||
<link rel=\"stylesheet\">\
|
||||
<style></style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<div></div>\
|
||||
</body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
#[test]
|
||||
fn passing_no_images() {
|
||||
let html = "<link rel=\"icon\" href=\"favicon.ico\">\
|
||||
<div><img src=\"http://localhost/assets/mono_lisa.png\" /></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
format!(
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html>\
|
||||
<head>\
|
||||
<link rel=\"icon\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<div>\
|
||||
<img src=\"{empty_image}\">\
|
||||
</div>\
|
||||
</body>\
|
||||
</html>",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_no_body_background_images() {
|
||||
let html = "<body background=\"no/such/image.png\" background=\"no/such/image2.png\"></body>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_no_frames() {
|
||||
let html = "<frameset><frame src=\"http://trackbook.com\"></frameset>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><frameset><frame src=\"\"></frameset></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_no_iframes() {
|
||||
let html = "<iframe src=\"http://trackbook.com\"></iframe>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><iframe src=\"\"></iframe></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_no_js() {
|
||||
let html = "<div onClick=\"void(0)\">\
|
||||
<script src=\"http://localhost/assets/some.js\"></script>\
|
||||
<script>alert(1)</script>\
|
||||
</div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><script></script>\
|
||||
<script></script></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_with_no_integrity() {
|
||||
let html = "<title>No integrity</title>\
|
||||
<link integrity=\"sha384-...\" rel=\"something\"/>\
|
||||
<script integrity=\"sha384-...\" src=\"some.js\"></script>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html>\
|
||||
<head><title>No integrity</title><link rel=\"something\"><script></script></head>\
|
||||
<body></body>\
|
||||
</html>"
|
||||
);
|
||||
<head>\
|
||||
<link rel=\"stylesheet\">\
|
||||
<style></style>\
|
||||
</head>\
|
||||
<body>\
|
||||
<div></div>\
|
||||
</body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_images() {
|
||||
let html = "<link rel=\"icon\" href=\"favicon.ico\">\
|
||||
<div><img src=\"http://localhost/assets/mono_lisa.png\" /></div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
format!(
|
||||
"<html>\
|
||||
<head>\
|
||||
<link rel=\"icon\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<div>\
|
||||
<img src=\"{empty_image}\">\
|
||||
</div>\
|
||||
</body>\
|
||||
</html>",
|
||||
empty_image = empty_image!()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_body_background_images() {
|
||||
let html =
|
||||
"<body background=\"no/such/image.png\" background=\"no/such/image2.png\"></body>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_frames() {
|
||||
let html = "<frameset><frame src=\"http://trackbook.com\"></frameset>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><frameset><frame src=\"\"></frameset></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_iframes() {
|
||||
let html = "<iframe src=\"http://trackbook.com\"></iframe>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = false;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><iframe src=\"\"></iframe></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_js() {
|
||||
let html = "<div onClick=\"void(0)\">\
|
||||
<script src=\"http://localhost/assets/some.js\"></script>\
|
||||
<script>alert(1)</script>\
|
||||
</div>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
|
||||
let opt_no_css: bool = false;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = false;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = false;
|
||||
let opt_silent = true;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html><head></head><body><div><script></script>\
|
||||
<script></script></div></body></html>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_no_integrity() {
|
||||
let html = "<title>No integrity</title>\
|
||||
<link integrity=\"sha384-...\" rel=\"something\"/>\
|
||||
<script integrity=\"sha384-...\" src=\"some.js\"></script>";
|
||||
let dom = html::html_to_dom(&html);
|
||||
let url = "http://localhost";
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
let opt_no_css: bool = true;
|
||||
let opt_no_fonts: bool = false;
|
||||
let opt_no_frames: bool = true;
|
||||
let opt_no_js: bool = true;
|
||||
let opt_no_images: bool = true;
|
||||
let opt_silent = true;
|
||||
|
||||
html::walk_and_embed_assets(
|
||||
cache,
|
||||
&client,
|
||||
&url,
|
||||
&dom.document,
|
||||
opt_no_css,
|
||||
opt_no_fonts,
|
||||
opt_no_frames,
|
||||
opt_no_js,
|
||||
opt_no_images,
|
||||
opt_silent,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
serialize(&mut buf, &dom.document, SerializeOpts::default()).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
buf.iter().map(|&c| c as char).collect::<String>(),
|
||||
"<html>\
|
||||
<head><title>No integrity</title><link rel=\"something\"><script></script></head>\
|
||||
<body></body>\
|
||||
</html>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::js;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,19 +5,24 @@ use crate::js;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_onblur_camelcase() {
|
||||
assert!(js::attr_is_event_handler("onBlur"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::js;
|
||||
|
||||
#[test]
|
||||
fn passing_onclick_lowercase() {
|
||||
assert!(js::attr_is_event_handler("onclick"));
|
||||
}
|
||||
#[test]
|
||||
fn onblur_camelcase() {
|
||||
assert!(js::attr_is_event_handler("onBlur"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_onclick_camelcase() {
|
||||
assert!(js::attr_is_event_handler("onClick"));
|
||||
#[test]
|
||||
fn onclick_lowercase() {
|
||||
assert!(js::attr_is_event_handler("onclick"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn onclick_camelcase() {
|
||||
assert!(js::attr_is_event_handler("onClick"));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -29,17 +32,22 @@ fn passing_onclick_camelcase() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_href() {
|
||||
assert!(!js::attr_is_event_handler("href"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::js;
|
||||
|
||||
#[test]
|
||||
fn failing_empty_string() {
|
||||
assert!(!js::attr_is_event_handler(""));
|
||||
}
|
||||
#[test]
|
||||
fn href() {
|
||||
assert!(!js::attr_is_event_handler("href"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_class() {
|
||||
assert!(!js::attr_is_event_handler("class"));
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert!(!js::attr_is_event_handler(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class() {
|
||||
assert!(!js::attr_is_event_handler("class"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,42 +5,47 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_removes_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot#iefix"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_removes_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot#"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn removes_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot#iefix"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_removes_empty_query_and_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot?#"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn removes_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot#"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_removes_empty_query_amp_and_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot?a=b&#"),
|
||||
"https://somewhere.com/font.eot?a=b"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn removes_empty_query_and_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot?#"),
|
||||
"https://somewhere.com/font.eot"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_keeps_credentials() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://cookie:monster@gibson.internet/"),
|
||||
"https://cookie:monster@gibson.internet/"
|
||||
);
|
||||
#[test]
|
||||
fn removes_empty_query_amp_and_empty_fragment() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://somewhere.com/font.eot?a=b&#"),
|
||||
"https://somewhere.com/font.eot?a=b"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_credentials() {
|
||||
assert_eq!(
|
||||
utils::clean_url("https://cookie:monster@gibson.internet/"),
|
||||
"https://cookie:monster@gibson.internet/"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,22 +5,27 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_encode_string_with_specific_media_type() {
|
||||
let mime = "application/javascript";
|
||||
let data = "var word = 'hello';\nalert(word);\n";
|
||||
let data_url = utils::data_to_data_url(mime, data.as_bytes(), "", "");
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
assert_eq!(
|
||||
&data_url,
|
||||
"data:application/javascript;base64,dmFyIHdvcmQgPSAnaGVsbG8nOwphbGVydCh3b3JkKTsK"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_encode_append_fragment() {
|
||||
let data = "<svg></svg>\n";
|
||||
let data_url = utils::data_to_data_url("text/css", data.as_bytes(), "", "fragment");
|
||||
|
||||
assert_eq!(&data_url, "data:text/css;base64,PHN2Zz48L3N2Zz4K#fragment");
|
||||
#[test]
|
||||
fn encode_string_with_specific_media_type() {
|
||||
let mime = "application/javascript";
|
||||
let data = "var word = 'hello';\nalert(word);\n";
|
||||
let data_url = utils::data_to_data_url(mime, data.as_bytes(), "", "");
|
||||
|
||||
assert_eq!(
|
||||
&data_url,
|
||||
"data:application/javascript;base64,dmFyIHdvcmQgPSAnaGVsbG8nOwphbGVydCh3b3JkKTsK"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_append_fragment() {
|
||||
let data = "<svg></svg>\n";
|
||||
let data_url = utils::data_to_data_url("text/css", data.as_bytes(), "", "fragment");
|
||||
|
||||
assert_eq!(&data_url, "data:text/css;base64,PHN2Zz48L3N2Zz4K#fragment");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,76 +5,82 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_parse_text_html_base64() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg==");
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn parse_text_html_base64() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg==");
|
||||
|
||||
#[test]
|
||||
fn passing_parse_text_html_utf8() {
|
||||
let (media_type, data) = utils::data_url_to_data(
|
||||
"data:text/html;utf8,Work expands so as to fill the time available for its completion",
|
||||
);
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn parse_text_html_utf8() {
|
||||
let (media_type, data) = utils::data_url_to_data(
|
||||
"data:text/html;utf8,Work expands so as to fill the time available for its completion",
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_parse_text_html_plaintext() {
|
||||
let (media_type, data) = utils::data_url_to_data(
|
||||
"data:text/html,Work expands so as to fill the time available for its completion",
|
||||
);
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn parse_text_html_plaintext() {
|
||||
let (media_type, data) = utils::data_url_to_data(
|
||||
"data:text/html,Work expands so as to fill the time available for its completion",
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_parse_text_html_charset_utf_8_between_two_whitespaces() {
|
||||
let (media_type, data) = utils::data_url_to_data(" data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion ");
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn parse_text_html_charset_utf_8_between_two_whitespaces() {
|
||||
let (media_type, data) = utils::data_url_to_data(" data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion ");
|
||||
|
||||
#[test]
|
||||
fn passing_parse_text_css_url_encoded() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:text/css,div{background-color:%23000}");
|
||||
assert_eq!(media_type, "text/html");
|
||||
assert_eq!(
|
||||
String::from_utf8_lossy(&data),
|
||||
"Work expands so as to fill the time available for its completion"
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "text/css");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "div{background-color:#000}");
|
||||
}
|
||||
#[test]
|
||||
fn parse_text_css_url_encoded() {
|
||||
let (media_type, data) =
|
||||
utils::data_url_to_data("data:text/css,div{background-color:%23000}");
|
||||
|
||||
#[test]
|
||||
fn passing_parse_no_media_type_base64() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:;base64,dGVzdA==");
|
||||
assert_eq!(media_type, "text/css");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "div{background-color:#000}");
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "test");
|
||||
}
|
||||
#[test]
|
||||
fn parse_no_media_type_base64() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:;base64,dGVzdA==");
|
||||
|
||||
#[test]
|
||||
fn passing_parse_no_media_type_no_encoding() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:;,test%20test");
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "test");
|
||||
}
|
||||
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "test test");
|
||||
#[test]
|
||||
fn parse_no_media_type_no_encoding() {
|
||||
let (media_type, data) = utils::data_url_to_data("data:;,test%20test");
|
||||
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "test test");
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -86,10 +90,15 @@ fn passing_parse_no_media_type_no_encoding() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_just_word_data() {
|
||||
let (media_type, data) = utils::data_url_to_data("data");
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "");
|
||||
#[test]
|
||||
fn just_word_data() {
|
||||
let (media_type, data) = utils::data_url_to_data("data");
|
||||
|
||||
assert_eq!(media_type, "");
|
||||
assert_eq!(String::from_utf8_lossy(&data), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,131 +5,136 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_image_gif87() {
|
||||
assert_eq!(utils::detect_media_type(b"GIF87a", ""), "image/gif");
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_image_gif89() {
|
||||
assert_eq!(utils::detect_media_type(b"GIF89a", ""), "image/gif");
|
||||
}
|
||||
#[test]
|
||||
fn image_gif87() {
|
||||
assert_eq!(utils::detect_media_type(b"GIF87a", ""), "image/gif");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_jpeg() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\xD8\xFF", ""), "image/jpeg");
|
||||
}
|
||||
#[test]
|
||||
fn image_gif89() {
|
||||
assert_eq!(utils::detect_media_type(b"GIF89a", ""), "image/gif");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_png() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x89PNG\x0D\x0A\x1A\x0A", ""),
|
||||
"image/png"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn image_jpeg() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\xD8\xFF", ""), "image/jpeg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_svg() {
|
||||
assert_eq!(utils::detect_media_type(b"<svg ", ""), "image/svg+xml");
|
||||
}
|
||||
#[test]
|
||||
fn image_png() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x89PNG\x0D\x0A\x1A\x0A", ""),
|
||||
"image/png"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_webp() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....WEBPVP8 ", ""),
|
||||
"image/webp"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn image_svg() {
|
||||
assert_eq!(utils::detect_media_type(b"<svg ", ""), "image/svg+xml");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_icon() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x00\x00\x01\x00", ""),
|
||||
"image/x-icon"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn image_webp() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....WEBPVP8 ", ""),
|
||||
"image/webp"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_svg_filename() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"<?xml ", "local-file.svg"),
|
||||
"image/svg+xml"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn image_icon() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x00\x00\x01\x00", ""),
|
||||
"image/x-icon"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_image_svg_url_uppercase() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"", "https://some-site.com/images/local-file.SVG"),
|
||||
"image/svg+xml"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn image_svg_filename() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"<?xml ", "local-file.svg"),
|
||||
"image/svg+xml"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_mpeg() {
|
||||
assert_eq!(utils::detect_media_type(b"ID3", ""), "audio/mpeg");
|
||||
}
|
||||
#[test]
|
||||
fn image_svg_url_uppercase() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"", "https://some-site.com/images/local-file.SVG"),
|
||||
"image/svg+xml"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_mpeg_2() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\x0E", ""), "audio/mpeg");
|
||||
}
|
||||
#[test]
|
||||
fn audio_mpeg() {
|
||||
assert_eq!(utils::detect_media_type(b"ID3", ""), "audio/mpeg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_mpeg_3() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\x0F", ""), "audio/mpeg");
|
||||
}
|
||||
#[test]
|
||||
fn audio_mpeg_2() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\x0E", ""), "audio/mpeg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_ogg() {
|
||||
assert_eq!(utils::detect_media_type(b"OggS", ""), "audio/ogg");
|
||||
}
|
||||
#[test]
|
||||
fn audio_mpeg_3() {
|
||||
assert_eq!(utils::detect_media_type(b"\xFF\x0F", ""), "audio/mpeg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_wav() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....WAVEfmt ", ""),
|
||||
"audio/wav"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn audio_ogg() {
|
||||
assert_eq!(utils::detect_media_type(b"OggS", ""), "audio/ogg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_audio_flac() {
|
||||
assert_eq!(utils::detect_media_type(b"fLaC", ""), "audio/x-flac");
|
||||
}
|
||||
#[test]
|
||||
fn audio_wav() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....WAVEfmt ", ""),
|
||||
"audio/wav"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_video_avi() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....AVI LIST", ""),
|
||||
"video/avi"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn audio_flac() {
|
||||
assert_eq!(utils::detect_media_type(b"fLaC", ""), "audio/x-flac");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_video_mp4() {
|
||||
assert_eq!(utils::detect_media_type(b"....ftyp", ""), "video/mp4");
|
||||
}
|
||||
#[test]
|
||||
fn video_avi() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"RIFF....AVI LIST", ""),
|
||||
"video/avi"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_video_mpeg() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x00\x00\x01\x0B", ""),
|
||||
"video/mpeg"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn video_mp4() {
|
||||
assert_eq!(utils::detect_media_type(b"....ftyp", ""), "video/mp4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_video_quicktime() {
|
||||
assert_eq!(utils::detect_media_type(b"....moov", ""), "video/quicktime");
|
||||
}
|
||||
#[test]
|
||||
fn video_mpeg() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x00\x00\x01\x0B", ""),
|
||||
"video/mpeg"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_video_webm() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x1A\x45\xDF\xA3", ""),
|
||||
"video/webm"
|
||||
);
|
||||
#[test]
|
||||
fn video_quicktime() {
|
||||
assert_eq!(utils::detect_media_type(b"....moov", ""), "video/quicktime");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn video_webm() {
|
||||
assert_eq!(
|
||||
utils::detect_media_type(b"\x1A\x45\xDF\xA3", ""),
|
||||
"video/webm"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -141,7 +144,12 @@ fn passing_video_webm() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_unknown_media_type() {
|
||||
assert_eq!(utils::detect_media_type(b"abcdef0123456789", ""), "");
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn unknown_media_type() {
|
||||
assert_eq!(utils::detect_media_type(b"abcdef0123456789", ""), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,32 +5,37 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_remove_protocl_and_fragment() {
|
||||
if cfg!(windows) {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///C:/documents/some-path/some-file.svg#fragment"),
|
||||
"C:\\documents\\some-path\\some-file.svg"
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///tmp/some-path/some-file.svg#fragment"),
|
||||
"/tmp/some-path/some-file.svg"
|
||||
);
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_decodes_urls() {
|
||||
if cfg!(windows) {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///C:/Documents%20and%20Settings/some-file.html"),
|
||||
"C:\\Documents and Settings\\some-file.html"
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///home/user/My%20Documents"),
|
||||
"/home/user/My Documents"
|
||||
);
|
||||
#[test]
|
||||
fn remove_protocl_and_fragment() {
|
||||
if cfg!(windows) {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///C:/documents/some-path/some-file.svg#fragment"),
|
||||
"C:\\documents\\some-path\\some-file.svg"
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///tmp/some-path/some-file.svg#fragment"),
|
||||
"/tmp/some-path/some-file.svg"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decodes_urls() {
|
||||
if cfg!(windows) {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///C:/Documents%20and%20Settings/some-file.html"),
|
||||
"C:\\Documents and Settings\\some-file.html"
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
utils::file_url_to_fs_path("file:///home/user/My%20Documents"),
|
||||
"/home/user/My Documents"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,17 +5,22 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_data_url() {
|
||||
assert_eq!(
|
||||
utils::get_url_fragment(
|
||||
"data:image/svg+xml;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h#test"
|
||||
),
|
||||
"test"
|
||||
);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_https_empty() {
|
||||
assert_eq!(utils::get_url_fragment("https://kernel.org#"), "");
|
||||
#[test]
|
||||
fn data_url() {
|
||||
assert_eq!(
|
||||
utils::get_url_fragment(
|
||||
"data:image/svg+xml;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h#test"
|
||||
),
|
||||
"test"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn https_empty() {
|
||||
assert_eq!(utils::get_url_fragment("https://kernel.org#"), "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,18 +5,23 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_data_url_text_html() {
|
||||
assert!(utils::is_data_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_data_url_no_media_type() {
|
||||
assert!(utils::is_data_url(
|
||||
"data:;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
#[test]
|
||||
fn data_url_text_html() {
|
||||
assert!(utils::is_data_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_url_no_media_type() {
|
||||
assert!(utils::is_data_url(
|
||||
"data:;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -28,17 +31,22 @@ fn passing_data_url_no_media_type() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_https_url() {
|
||||
assert!(!utils::is_data_url("https://kernel.org"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn failing_no_protocol_url() {
|
||||
assert!(!utils::is_data_url("//kernel.org"));
|
||||
}
|
||||
#[test]
|
||||
fn https_url() {
|
||||
assert!(!utils::is_data_url("https://kernel.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_empty_string() {
|
||||
assert!(!utils::is_data_url(""));
|
||||
#[test]
|
||||
fn no_protocol_url() {
|
||||
assert!(!utils::is_data_url("//kernel.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert!(!utils::is_data_url(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,32 +5,37 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_unix_file_url() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:///home/user/Websites/my-website/index.html"
|
||||
));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_windows_file_url() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:///C:/Documents%20and%20Settings/user/Websites/my-website/assets/images/logo.png"
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn unix_file_url() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:///home/user/Websites/my-website/index.html"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_unix_url_with_backslashes() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:\\\\\\home\\user\\Websites\\my-website\\index.html"
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn windows_file_url() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:///C:/Documents%20and%20Settings/user/Websites/my-website/assets/images/logo.png"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_windows_file_url_with_backslashes() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:\\\\\\C:\\Documents%20and%20Settings\\user\\Websites\\my-website\\assets\\images\\logo.png"
|
||||
));
|
||||
#[test]
|
||||
fn unix_url_with_backslashes() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:\\\\\\home\\user\\Websites\\my-website\\index.html"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_file_url_with_backslashes() {
|
||||
assert!(utils::is_file_url(
|
||||
"file:\\\\\\C:\\Documents%20and%20Settings\\user\\Websites\\my-website\\assets\\images\\logo.png"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -42,34 +45,39 @@ fn passing_windows_file_url_with_backslashes() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_url_with_no_protocl() {
|
||||
assert!(!utils::is_file_url("//kernel.org"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn failing_dot_slash_filename() {
|
||||
assert!(!utils::is_file_url("./index.html"));
|
||||
}
|
||||
#[test]
|
||||
fn url_with_no_protocl() {
|
||||
assert!(!utils::is_file_url("//kernel.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_just_filename() {
|
||||
assert!(!utils::is_file_url("some-local-page.htm"));
|
||||
}
|
||||
#[test]
|
||||
fn dot_slash_filename() {
|
||||
assert!(!utils::is_file_url("./index.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_https_ip_port_url() {
|
||||
assert!(!utils::is_file_url("https://1.2.3.4:80/www/index.html"));
|
||||
}
|
||||
#[test]
|
||||
fn just_filename() {
|
||||
assert!(!utils::is_file_url("some-local-page.htm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_data_url() {
|
||||
assert!(!utils::is_file_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn https_ip_port_url() {
|
||||
assert!(!utils::is_file_url("https://1.2.3.4:80/www/index.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_just_word_file() {
|
||||
assert!(!utils::is_file_url("file"));
|
||||
#[test]
|
||||
fn data_url() {
|
||||
assert!(!utils::is_file_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn just_word_file() {
|
||||
assert!(!utils::is_file_url("file"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,19 +5,24 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_http_url() {
|
||||
assert!(utils::is_http_url("http://kernel.org"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_https_url() {
|
||||
assert!(utils::is_http_url("https://www.rust-lang.org/"));
|
||||
}
|
||||
#[test]
|
||||
fn http_url() {
|
||||
assert!(utils::is_http_url("http://kernel.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_http_url_with_backslashes() {
|
||||
assert!(utils::is_http_url("http:\\\\freebsd.org\\"));
|
||||
#[test]
|
||||
fn https_url() {
|
||||
assert!(utils::is_http_url("https://www.rust-lang.org/"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn http_url_with_backslashes() {
|
||||
assert!(utils::is_http_url("http:\\\\freebsd.org\\"));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -29,29 +32,34 @@ fn passing_http_url_with_backslashes() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_url_with_no_protocol() {
|
||||
assert!(!utils::is_http_url("//kernel.org"));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn failing_dot_slash_filename() {
|
||||
assert!(!utils::is_http_url("./index.html"));
|
||||
}
|
||||
#[test]
|
||||
fn url_with_no_protocol() {
|
||||
assert!(!utils::is_http_url("//kernel.org"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_just_filename() {
|
||||
assert!(!utils::is_http_url("some-local-page.htm"));
|
||||
}
|
||||
#[test]
|
||||
fn dot_slash_filename() {
|
||||
assert!(!utils::is_http_url("./index.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_https_ip_port_url() {
|
||||
assert!(!utils::is_http_url("ftp://1.2.3.4/www/index.html"));
|
||||
}
|
||||
#[test]
|
||||
fn just_filename() {
|
||||
assert!(!utils::is_http_url("some-local-page.htm"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_data_url() {
|
||||
assert!(!utils::is_http_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
#[test]
|
||||
fn https_ip_port_url() {
|
||||
assert!(!utils::is_http_url("ftp://1.2.3.4/www/index.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_url() {
|
||||
assert!(!utils::is_http_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
use url::ParseError;
|
||||
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -9,203 +5,210 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_from_https_to_level_up_relative() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url("https://www.kernel.org", "../category/signatures.html")?;
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
use url::ParseError;
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
#[test]
|
||||
fn from_https_to_level_up_relative() -> Result<(), ParseError> {
|
||||
let resolved_url =
|
||||
utils::resolve_url("https://www.kernel.org", "../category/signatures.html")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_just_filename_to_full_https_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"saved_page.htm",
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
#[test]
|
||||
fn from_just_filename_to_full_https_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"saved_page.htm",
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_https_url_to_url_with_no_protocol() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org",
|
||||
"//www.kernel.org/theme/images/logos/tux.png",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/theme/images/logos/tux.png"
|
||||
);
|
||||
#[test]
|
||||
fn from_https_url_to_url_with_no_protocol() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org",
|
||||
"//www.kernel.org/theme/images/logos/tux.png",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/theme/images/logos/tux.png"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_https_url_to_url_with_no_protocol_and_on_different_hostname(
|
||||
) -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org",
|
||||
"//another-host.org/theme/images/logos/tux.png",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://another-host.org/theme/images/logos/tux.png"
|
||||
);
|
||||
#[test]
|
||||
fn from_https_url_to_url_with_no_protocol_and_on_different_hostname() -> Result<(), ParseError>
|
||||
{
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org",
|
||||
"//another-host.org/theme/images/logos/tux.png",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://another-host.org/theme/images/logos/tux.png"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_https_url_to_relative_root_path() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
"/theme/images/logos/tux.png",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/theme/images/logos/tux.png"
|
||||
);
|
||||
#[test]
|
||||
fn from_https_url_to_relative_root_path() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
"/theme/images/logos/tux.png",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/theme/images/logos/tux.png"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_https_to_just_filename() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.w3schools.com/html/html_iframe.asp",
|
||||
"default.asp",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.w3schools.com/html/default.asp"
|
||||
);
|
||||
#[test]
|
||||
fn from_https_to_just_filename() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"https://www.w3schools.com/html/html_iframe.asp",
|
||||
"default.asp",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.w3schools.com/html/default.asp"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_data_url_to_https() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
#[test]
|
||||
fn from_data_url_to_https() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"https://www.kernel.org/category/signatures.html",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"https://www.kernel.org/category/signatures.html"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_data_url_to_data_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K",
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K"
|
||||
);
|
||||
#[test]
|
||||
fn from_data_url_to_data_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_file_url_to_relative_path() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"file:///home/user/Websites/my-website/index.html",
|
||||
"assets/images/logo.png",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"file:///home/user/Websites/my-website/assets/images/logo.png"
|
||||
);
|
||||
#[test]
|
||||
fn from_file_url_to_relative_path() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"file:///home/user/Websites/my-website/index.html",
|
||||
"assets/images/logo.png",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"file:///home/user/Websites/my-website/assets/images/logo.png"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_file_url_to_relative_path_with_backslashes() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"file:\\\\\\home\\user\\Websites\\my-website\\index.html",
|
||||
"assets\\images\\logo.png",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"file:///home/user/Websites/my-website/assets/images/logo.png"
|
||||
);
|
||||
#[test]
|
||||
fn from_file_url_to_relative_path_with_backslashes() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"file:\\\\\\home\\user\\Websites\\my-website\\index.html",
|
||||
"assets\\images\\logo.png",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"file:///home/user/Websites/my-website/assets/images/logo.png"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_from_data_url_to_file_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"file:///etc/passwd",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(resolved_url.as_str(), "file:///etc/passwd");
|
||||
#[test]
|
||||
fn from_data_url_to_file_url() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"file:///etc/passwd",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(resolved_url.as_str(), "file:///etc/passwd");
|
||||
|
||||
#[test]
|
||||
fn passing_preserve_fragment() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"http://doesnt-matter.local/",
|
||||
"css/fonts/fontmarvelous.svg#fontmarvelous",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"http://doesnt-matter.local/css/fonts/fontmarvelous.svg#fontmarvelous"
|
||||
);
|
||||
#[test]
|
||||
fn preserve_fragment() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"http://doesnt-matter.local/",
|
||||
"css/fonts/fontmarvelous.svg#fontmarvelous",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
"http://doesnt-matter.local/css/fonts/fontmarvelous.svg#fontmarvelous"
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn passing_resolve_from_file_url_to_file_url() -> Result<(), ParseError> {
|
||||
let resolved_url = if cfg!(windows) {
|
||||
utils::resolve_url("file:///c:/index.html", "file:///c:/image.png").unwrap_or(str!())
|
||||
} else {
|
||||
utils::resolve_url("file:///tmp/index.html", "file:///tmp/image.png").unwrap_or(str!())
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
if cfg!(windows) {
|
||||
"file:///c:/image.png"
|
||||
#[test]
|
||||
fn resolve_from_file_url_to_file_url() -> Result<(), ParseError> {
|
||||
let resolved_url = if cfg!(windows) {
|
||||
utils::resolve_url("file:///c:/index.html", "file:///c:/image.png").unwrap_or(str!())
|
||||
} else {
|
||||
"file:///tmp/image.png"
|
||||
}
|
||||
);
|
||||
utils::resolve_url("file:///tmp/index.html", "file:///tmp/image.png").unwrap_or(str!())
|
||||
};
|
||||
|
||||
Ok(())
|
||||
assert_eq!(
|
||||
resolved_url.as_str(),
|
||||
if cfg!(windows) {
|
||||
"file:///c:/image.png"
|
||||
} else {
|
||||
"file:///tmp/image.png"
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -215,15 +218,21 @@ fn passing_resolve_from_file_url_to_file_url() -> Result<(), ParseError> {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_from_data_url_to_url_with_no_protocol() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"//www.w3schools.com/html/html_iframe.asp",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
use url::ParseError;
|
||||
|
||||
assert_eq!(resolved_url.as_str(), "");
|
||||
#[test]
|
||||
fn from_data_url_to_url_with_no_protocol() -> Result<(), ParseError> {
|
||||
let resolved_url = utils::resolve_url(
|
||||
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
|
||||
"//www.w3schools.com/html/html_iframe.asp",
|
||||
)
|
||||
.unwrap_or(str!());
|
||||
|
||||
Ok(())
|
||||
assert_eq!(resolved_url.as_str(), "");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
use crate::utils;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -10,66 +5,74 @@ use std::env;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_read_data_url() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
|
||||
// If both source and target are data URLs,
|
||||
// ensure the result contains target data URL
|
||||
let (data, final_url, media_type) = utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"data:text/html;base64,c291cmNl",
|
||||
"data:text/html;base64,dGFyZ2V0",
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
utils::data_to_data_url(&media_type, &data, &final_url, ""),
|
||||
utils::data_to_data_url("text/html", "target".as_bytes(), "", "")
|
||||
);
|
||||
assert_eq!(
|
||||
final_url,
|
||||
utils::data_to_data_url("text/html", "target".as_bytes(), "", "")
|
||||
);
|
||||
assert_eq!(&media_type, "text/html");
|
||||
}
|
||||
#[test]
|
||||
fn read_data_url() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
#[test]
|
||||
fn passing_read_local_file_with_file_url_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// Inclusion of local assets from local sources should be allowed
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let (data, final_url, _media_type) = utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
),
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-script.js",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(utils::data_to_data_url("application/javascript", &data, &final_url, ""), "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==");
|
||||
assert_eq!(
|
||||
&final_url,
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-script.js",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
// If both source and target are data URLs,
|
||||
// ensure the result contains target data URL
|
||||
let (data, final_url, media_type) = utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"data:text/html;base64,c291cmNl",
|
||||
"data:text/html;base64,dGFyZ2V0",
|
||||
false,
|
||||
)
|
||||
);
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
utils::data_to_data_url(&media_type, &data, &final_url, ""),
|
||||
utils::data_to_data_url("text/html", "target".as_bytes(), "", "")
|
||||
);
|
||||
assert_eq!(
|
||||
final_url,
|
||||
utils::data_to_data_url("text/html", "target".as_bytes(), "", "")
|
||||
);
|
||||
assert_eq!(&media_type, "text/html");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_local_file_with_file_url_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" };
|
||||
|
||||
// Inclusion of local assets from local sources should be allowed
|
||||
let cwd = env::current_dir().unwrap();
|
||||
let (data, final_url, _media_type) = utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-file.html",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
),
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-script.js",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(utils::data_to_data_url("application/javascript", &data, &final_url, ""), "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==");
|
||||
assert_eq!(
|
||||
&final_url,
|
||||
&format!(
|
||||
"{file}{cwd}/src/tests/data/basic/local-script.js",
|
||||
file = file_url_protocol,
|
||||
cwd = cwd.to_str().unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -79,46 +82,53 @@ fn passing_read_local_file_with_file_url_parent() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_read_local_file_with_data_url_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
use reqwest::blocking::Client;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Inclusion of local assets from data URL sources should not be allowed
|
||||
match utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"data:text/html;base64,SoUrCe",
|
||||
"file:///etc/passwd",
|
||||
false,
|
||||
) {
|
||||
Ok((..)) => {
|
||||
assert!(false);
|
||||
#[test]
|
||||
fn read_local_file_with_data_url_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
// Inclusion of local assets from data URL sources should not be allowed
|
||||
match utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"data:text/html;base64,SoUrCe",
|
||||
"file:///etc/passwd",
|
||||
false,
|
||||
) {
|
||||
Ok((..)) => {
|
||||
assert!(false);
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_read_local_file_with_https_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
// Inclusion of local assets from remote sources should not be allowed
|
||||
match utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"https://kernel.org/",
|
||||
"file:///etc/passwd",
|
||||
false,
|
||||
) {
|
||||
Ok((..)) => {
|
||||
assert!(false);
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_local_file_with_https_parent() {
|
||||
let cache = &mut HashMap::new();
|
||||
let client = Client::new();
|
||||
|
||||
// Inclusion of local assets from remote sources should not be allowed
|
||||
match utils::retrieve_asset(
|
||||
cache,
|
||||
&client,
|
||||
"https://kernel.org/",
|
||||
"file:///etc/passwd",
|
||||
false,
|
||||
) {
|
||||
Ok((..)) => {
|
||||
assert!(false);
|
||||
}
|
||||
Err(_) => {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::utils;
|
||||
|
||||
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
|
||||
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
|
||||
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
|
||||
@@ -7,50 +5,55 @@ use crate::utils;
|
||||
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn passing_mailto() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"mailto:somebody@somewhere.com?subject=hello"
|
||||
));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod passing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn passing_tel() {
|
||||
assert!(utils::url_has_protocol("tel:5551234567"));
|
||||
}
|
||||
#[test]
|
||||
fn mailto() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"mailto:somebody@somewhere.com?subject=hello"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_ftp_no_slashes() {
|
||||
assert!(utils::url_has_protocol("ftp:some-ftp-server.com"));
|
||||
}
|
||||
#[test]
|
||||
fn tel() {
|
||||
assert!(utils::url_has_protocol("tel:5551234567"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_ftp_with_credentials() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"ftp://user:password@some-ftp-server.com"
|
||||
));
|
||||
}
|
||||
#[test]
|
||||
fn ftp_no_slashes() {
|
||||
assert!(utils::url_has_protocol("ftp:some-ftp-server.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_javascript() {
|
||||
assert!(utils::url_has_protocol("javascript:void(0)"));
|
||||
}
|
||||
#[test]
|
||||
fn ftp_with_credentials() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"ftp://user:password@some-ftp-server.com"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_http() {
|
||||
assert!(utils::url_has_protocol("http://news.ycombinator.com"));
|
||||
}
|
||||
#[test]
|
||||
fn javascript() {
|
||||
assert!(utils::url_has_protocol("javascript:void(0)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_https() {
|
||||
assert!(utils::url_has_protocol("https://github.com"));
|
||||
}
|
||||
#[test]
|
||||
fn http() {
|
||||
assert!(utils::url_has_protocol("http://news.ycombinator.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passing_mailto_uppercase() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"MAILTO:somebody@somewhere.com?subject=hello"
|
||||
));
|
||||
#[test]
|
||||
fn https() {
|
||||
assert!(utils::url_has_protocol("https://github.com"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mailto_uppercase() {
|
||||
assert!(utils::url_has_protocol(
|
||||
"MAILTO:somebody@somewhere.com?subject=hello"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
@@ -60,24 +63,29 @@ fn passing_mailto_uppercase() {
|
||||
// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝
|
||||
// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
|
||||
|
||||
#[test]
|
||||
fn failing_url_with_no_protocol() {
|
||||
assert!(!utils::url_has_protocol(
|
||||
"//some-hostname.com/some-file.html"
|
||||
));
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod failing {
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn failing_relative_path() {
|
||||
assert!(!utils::url_has_protocol("some-hostname.com/some-file.html"));
|
||||
}
|
||||
#[test]
|
||||
fn url_with_no_protocol() {
|
||||
assert!(!utils::url_has_protocol(
|
||||
"//some-hostname.com/some-file.html"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_relative_to_root_path() {
|
||||
assert!(!utils::url_has_protocol("/some-file.html"));
|
||||
}
|
||||
#[test]
|
||||
fn relative_path() {
|
||||
assert!(!utils::url_has_protocol("some-hostname.com/some-file.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failing_empty_string() {
|
||||
assert!(!utils::url_has_protocol(""));
|
||||
#[test]
|
||||
fn relative_to_root_path() {
|
||||
assert!(!utils::url_has_protocol("/some-file.html"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_string() {
|
||||
assert!(!utils::url_has_protocol(""));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user