summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatan Kushner <hello@matchai.dev>2020-01-26 17:37:18 -0500
committerKevin Song <chipbuster@users.noreply.github.com>2020-01-26 16:37:18 -0600
commit3365beae09af2f12b6323a52f88633ed97b39c51 (patch)
treefb36ec6c06439edd29775fc4a7b1a06041619b69
parent5342dcc658110f8884a77ef1b3f83e867322ffb5 (diff)
test(nodejs): Port nodejs module tests from E2E to integraton (#867)
Replaces the existing nodejs module end-to-end tests with integration tests that don't require preinstalled environmental dependencies. - Moved the tests to the same file as the module they test - Created a render_module utility function for rendering modules within tests - Removed Node.js installation during CI setup - Add Shell to Context to allow for tests to not run shell-specific code
-rw-r--r--.github/workflows/workflow.yml5
-rw-r--r--src/context.rs28
-rw-r--r--src/module.rs36
-rw-r--r--src/modules/battery.rs7
-rw-r--r--src/modules/character.rs8
-rw-r--r--src/modules/memory_usage.rs7
-rw-r--r--src/modules/mod.rs2
-rw-r--r--src/modules/nodejs.rs52
-rw-r--r--src/modules/utils/mod.rs3
-rw-r--r--src/modules/utils/test.rs12
-rw-r--r--src/print.rs19
-rw-r--r--src/utils.rs5
-rw-r--r--tests/testsuite/main.rs1
-rw-r--r--tests/testsuite/nodejs.rs93
14 files changed, 136 insertions, 142 deletions
diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index c4900c6b8..ac281d0b1 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -133,11 +133,6 @@ jobs:
ARGS: --resolver nightly-2019-09-21
run: stack $ARGS ghc -- --numeric-version --no-install-ghc
- # Install Node.js at a fixed version
- - uses: actions/setup-node@v1
- with:
- node-version: "12.0.0"
-
# Install Golang at a fixed version
- uses: actions/setup-go@v1
with:
diff --git a/src/context.rs b/src/context.rs
index ead8e1e79..460ede159 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -31,6 +31,9 @@ pub struct Context<'a> {
/// Private field to store Git information for modules who need it
repo: OnceCell<Repo>,
+
+ /// The shell the user is assumed to be running
+ pub shell: Shell,
}
impl<'a> Context<'a> {
@@ -71,12 +74,15 @@ impl<'a> Context<'a> {
// TODO: Currently gets the physical directory. Get the logical directory.
let current_dir = Context::expand_tilde(dir.into());
+ let shell = Context::get_shell();
+
Context {
config,
properties,
current_dir,
dir_files: OnceCell::new(),
repo: OnceCell::new(),
+ shell,
}
}
@@ -160,6 +166,18 @@ impl<'a> Context<'a> {
Ok(dir_files)
})
}
+
+ fn get_shell() -> Shell {
+ let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default();
+ match shell.as_str() {
+ "bash" => Shell::Bash,
+ "fish" => Shell::Fish,
+ "ion" => Shell::Ion,
+ "powershell" => Shell::PowerShell,
+ "zsh" => Shell::Zsh,
+ _ => Shell::Unknown,
+ }
+ }
}
pub struct Repo {
@@ -252,6 +270,16 @@ fn get_current_branch(repository: &Repository) -> Option<String> {
shorthand.map(std::string::ToString::to_string)
}
+#[derive(Debug, Clone)]
+pub enum Shell {
+ Bash,
+ Fish,
+ Ion,
+ PowerShell,
+ Zsh,
+ Unknown,
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/module.rs b/src/module.rs
index 34f3a6520..f09c6e5df 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -1,4 +1,5 @@
use crate::config::SegmentConfig;
+use crate::context::Shell;
use crate::segment::Segment;
use ansi_term::Style;
use ansi_term::{ANSIString, ANSIStrings};
@@ -134,10 +135,10 @@ impl<'a> Module<'a> {
/// Returns a vector of colored ANSIString elements to be later used with
/// `ANSIStrings()` to optimize ANSI codes
pub fn ansi_strings(&self) -> Vec<ANSIString> {
- self.ansi_strings_for_prompt(true)
+ self.ansi_strings_for_shell(Shell::Unknown)
}
- pub fn ansi_strings_for_prompt(&self, is_prompt: bool) -> Vec<ANSIString> {
+ pub fn ansi_strings_for_shell(&self, shell: Shell) -> Vec<ANSIString> {
let mut ansi_strings = self
.segments
.iter()
@@ -147,20 +148,17 @@ impl<'a> Module<'a> {
ansi_strings.insert(0, self.prefix.ansi_string());
ansi_strings.push(self.suffix.ansi_string());
- if is_prompt {
- let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default();
- ansi_strings = match shell.as_str() {
- "bash" => ansi_strings_modified(ansi_strings, shell),
- "zsh" => ansi_strings_modified(ansi_strings, shell),
- _ => ansi_strings,
- };
- }
+ ansi_strings = match shell {
+ Shell::Bash => ansi_strings_modified(ansi_strings, shell),
+ Shell::Zsh => ansi_strings_modified(ansi_strings, shell),
+ _ => ansi_strings,
+ };
ansi_strings
}
- pub fn to_string_without_prefix(&self) -> String {
- ANSIStrings(&self.ansi_strings()[1..]).to_string()
+ pub fn to_string_without_prefix(&self, shell: Shell) -> String {
+ ANSIStrings(&self.ansi_strings_for_shell(shell)[1..]).to_string()
}
}
@@ -174,7 +172,7 @@ impl<'a> fmt::Display for Module<'a> {
/// Many shells cannot deal with raw unprintable characters (like ANSI escape sequences) and
/// miscompute the cursor position as a result, leading to strange visual bugs. Here, we wrap these
/// characters in shell-specific escape codes to indicate to the shell that they are zero-length.
-fn ansi_strings_modified(ansi_strings: Vec<ANSIString>, shell: String) -> Vec<ANSIString> {
+fn ansi_strings_modified(ansi_strings: Vec<ANSIString>, shell: Shell) -> Vec<ANSIString> {
const ESCAPE_BEGIN: char = '\u{1b}';
const MAYBE_ESCAPE_END: char = 'm';
ansi_strings
@@ -187,18 +185,18 @@ fn ansi_strings_modified(ansi_strings: Vec<ANSIString>, shell: String) -> Vec<AN
.map(|x| match x {
ESCAPE_BEGIN => {
escaped = true;
- match shell.as_str() {
- "bash" => String::from("\u{5c}\u{5b}\u{1b}"), // => \[ESC
- "zsh" => String::from("\u{25}\u{7b}\u{1b}"), // => %{ESC
+ match shell {
+ Shell::Bash => String::from("\u{5c}\u{5b}\u{1b}"), // => \[ESC
+ Shell::Zsh => String::from("\u{25}\u{7b}\u{1b}"), // => %{ESC
_ => x.to_string(),
}
}
MAYBE_ESCAPE_END => {
if escaped {
escaped = false;
- match shell.as_str() {
- "bash" => String::from("m\u{5c}\u{5d}"), // => m\]
- "zsh" => String::from("m\u{25}\u{7d}"), // => m%}
+ match shell {
+ Shell::Bash => String::from("m\u{5c}\u{5d}"), // => m\]
+ Shell::Zsh => String::from("m\u{25}\u{7d}"), // => m%}
_ => x.to_string(),
}
} else {
diff --git a/src/modules/battery.rs b/src/modules/battery.rs
index 8c651a954..55e248e57 100644
--- a/src/modules/battery.rs
+++ b/src/modules/battery.rs
@@ -1,13 +1,12 @@
-use super::{Context, Module, RootModuleConfig};
+use super::{Context, Module, RootModuleConfig, Shell};
use crate::configs::battery::BatteryConfig;
/// Creates a module for the battery percentage and charging state
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// TODO: Update when v1.0 printing refactor is implemented to only
// print escapes in a prompt context.
- let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default();
- let percentage_char = match shell.as_str() {
- "zsh" => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc`
+ let percentage_char = match context.shell {
+ Shell::Zsh => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc`
_ => "%",
};
diff --git a/src/modules/character.rs b/src/modules/character.rs
index cc9545172..e0c0ffe9e 100644
--- a/src/modules/character.rs
+++ b/src/modules/character.rs
@@ -1,5 +1,4 @@
-use super::{Context, Module, RootModuleConfig};
-
+use super::{Context, Module, RootModuleConfig, Shell};
use crate::configs::character::CharacterConfig;
/// Creates a module for the prompt character
@@ -25,7 +24,6 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let props = &context.properties;
let exit_code_default = std::string::String::from("0");
let exit_code = props.get("status_code").unwrap_or(&exit_code_default);
- let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default();
let keymap_default = std::string::String::from("viins");
let keymap = props.get("keymap").unwrap_or(&keymap_default);
let exit_success = exit_code == "0";
@@ -35,8 +33,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Unfortunately, this is also the name of the non-vi default mode.
// We do some environment detection in src/init.rs to translate.
// The result: in non-vi fish, keymap is always reported as "insert"
- let mode = match (shell.as_str(), keymap.as_str()) {
- ("fish", "default") | ("zsh", "vicmd") => ShellEditMode::Normal,
+ let mode = match (&context.shell, keymap.as_str()) {
+ (Shell::Fish, "default") | (Shell::Zsh, "vicmd") => ShellEditMode::Normal,
_ => ASSUMED_MODE,
};
diff --git a/src/modules/memory_usage.rs b/src/modules/memory_usage.rs
index 5a160ee48..c7d224339 100644
--- a/src/modules/memory_usage.rs
+++ b/src/modules/memory_usage.rs
@@ -1,7 +1,7 @@
use byte_unit::{Byte, ByteUnit};
use sysinfo::{RefreshKind, SystemExt};
-use super::{Context, Module, RootModuleConfig};
+use super::{Context, Module, RootModuleConfig, Shell};
use crate::configs::memory_usage::MemoryConfig;
@@ -19,9 +19,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// TODO: Update when v1.0 printing refactor is implemented to only
// print escapes in a prompt context.
- let shell = std::env::var("STARSHIP_SHELL").unwrap_or_default();
- let percent_sign = match shell.as_str() {
- "zsh" => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc`
+ let percent_sign = match context.shell {
+ Shell::Zsh => "%%", // % is an escape in zsh, see PROMPT in `man zshmisc`
_ => "%",
};
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index 9c9f403c0..74b11b946 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -35,7 +35,7 @@ mod utils;
mod battery;
use crate::config::{RootModuleConfig, SegmentConfig};
-use crate::context::Context;
+use crate::context::{Context, Shell};
use crate::module::Module;
pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
diff --git a/src/modules/nodejs.rs b/src/modules/nodejs.rs
index 5bd314354..7fec383fa 100644
--- a/src/modules/nodejs.rs
+++ b/src/modules/nodejs.rs
@@ -34,3 +34,55 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
Some(module)
}
+
+#[cfg(test)]
+mod tests {
+ use crate::modules::utils::test::render_module;
+ use ansi_term::Color;
+ use std::fs::{self, File};
+ use std::io;
+ use tempfile;
+
+ #[test]
+ fn folder_without_node_files() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let actual = render_module("nodejs", dir.path());
+ let expected = None;
+ assert_eq!(expected, actual);
+ Ok(())
+ }
+
+ #[test]
+ fn folder_with_package_json() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("package.json"))?.sync_all()?;
+
+ let actual = render_module("nodejs", dir.path());
+ let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
+ assert_eq!(expected, actual);
+ Ok(())
+ }
+
+ #[test]
+ fn folder_with_js_file() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ File::create(dir.path().join("index.js"))?.sync_all()?;
+
+ let actual = render_module("nodejs", dir.path());
+ let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
+ assert_eq!(expected, actual);
+ Ok(())
+ }
+
+ #[test]
+ fn folder_with_node_modules() -> io::Result<()> {
+ let dir = tempfile::tempdir()?;
+ let node_modules = dir.path().join("node_modules");
+ fs::create_dir_all(&node_modules)?;
+
+ let actual = render_module("nodejs", dir.path());
+ let expected = Some(format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0")));
+ assert_eq!(expected, actual);
+ Ok(())
+ }
+}
diff --git a/src/modules/utils/mod.rs b/src/modules/utils/mod.rs
index 6838fb19e..345ce7f40 100644
--- a/src/modules/utils/mod.rs
+++ b/src/modules/utils/mod.rs
@@ -1,2 +1,5 @@
pub mod directory;
pub mod java_version_parser;
+
+#[cfg(test)]
+pub mod test;
diff --git a/src/modules/utils/test.rs b/src/modules/utils/test.rs
new file mode 100644
index 000000000..b3c106b30
--- /dev/null
+++ b/src/modules/utils/test.rs
@@ -0,0 +1,12 @@
+use crate::config::StarshipConfig;
+use crate::context::{Context, Shell};
+use std::path::Path;
+
+/// Render a specific starship module by name
+pub fn render_module(module_name: &str, path: &Path) -> Option<String> {
+ let mut context = Context::new_with_dir(clap::ArgMatches::default(), path);
+ context.config = StarshipConfig { config: None };
+ context.shell = Shell::Unknown;
+
+ crate::print::get_module(module_name, context)
+}
diff --git a/src/print.rs b/src/print.rs
index ee3b36662..46d44d223 100644
--- a/src/print.rs
+++ b/src/print.rs
@@ -1,3 +1,4 @@
+use ansi_term::ANSIStrings;
use clap::ArgMatches;
use rayon::prelude::*;
use std::fmt::Write as FmtWrite;
@@ -35,10 +36,11 @@ pub fn get_prompt(context: Context) -> String {
for module in printable {
// Skip printing the prefix of a module after the line_break
if print_without_prefix {
- let module_without_prefix = module.to_string_without_prefix();
+ let module_without_prefix = module.to_string_without_prefix(context.shell.clone());
write!(buf, "{}", module_without_prefix).unwrap()
} else {
- write!(buf, "{}", module).unwrap();
+ let module = module.ansi_strings_for_shell(context.shell.clone());
+ write!(buf, "{}", ANSIStrings(&module)).unwrap();
}
print_without_prefix = module.get_name() == "line_break"
@@ -49,15 +51,14 @@ pub fn get_prompt(context: Context) -> String {
pub fn module(module_name: &str, args: ArgMatches) {
let context = Context::new(args);
-
- // If the module returns `None`, print an empty string
- let module = modules::handle(module_name, &context)
- .map(|m| m.to_string())
- .unwrap_or_default();
-
+ let module = get_module(module_name, context).unwrap_or_default();
print!("{}", module);
}
+pub fn get_module(module_name: &str, context: Context) -> Option<String> {
+ modules::handle(module_name, &context).map(|m| m.to_string())
+}
+
pub fn explain(args: ArgMatches) {
let context = Context::new(args);
@@ -73,7 +74,7 @@ pub fn explain(args: ArgMatches) {
.into_iter()
.filter(|module| !dont_print.contains(&module.get_name().as_str()))
.map(|module| {
- let ansi_strings = module.ansi_strings_for_prompt(false);
+ let ansi_strings = module.ansi_strings();
let value = module.get_segments().join("");
ModuleInfo {
value: ansi_term::ANSIStrings(&ansi_strings[1..ansi_strings.len() - 1]).to_string(),
diff --git a/src/utils.rs b/src/utils.rs
index e4a4e7d38..758aededc 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -36,8 +36,11 @@ pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> {
0 => String::from(cmd),
_ => format!("{} {}", cmd, args.join(" ")),
};
-
match command.as_str() {
+ "node --version" => Some(CommandOutput {
+ stdout: String::from("v12.0.0"),
+ stderr: String::default(),
+ }),
"dummy_command" => Some(CommandOutput {
stdout: String::from("stdout ok!"),
stderr: String::from("stderr ok!"),
diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs
index b7cce9435..f12b60780 100644
--- a/tests/testsuite/main.rs
+++ b/tests/testsuite/main.rs
@@ -19,7 +19,6 @@ mod jobs;
mod line_break;
mod modules;
mod nix_shell;
-mod nodejs;
mod python;
mod ruby;
mod terraform;
diff --git a/tests/testsuite/nodejs.rs b/tests/testsuite/nodejs.rs
deleted file mode 100644
index c0df62a7e..000000000
--- a/tests/testsuite/nodejs.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-use ansi_term::Color;
-use std::fs::{self, File};
-use std::io;
-use tempfile;
-
-use crate::common;
-
-/// Wrapper around common::render_module("nodejs") to work around platform quirks
-fn render_node_module() -> std::process::Command {
- let mut command = common::render_module("nodejs");
-
- // If SYSTEMROOT is not set on Windows node will refuse to print its version
- if cfg!(windows) {
- let system_root = std::env::var("SYSTEMROOT")
- .map(|i| {
- if i.trim().is_empty() {
- "C:\\WINDOWS".into()
- } else {
- i
- }
- })
- .unwrap_or_else(|_| "C:\\WINDOWS".into());
- command.env("SYSTEMROOT", system_root);
- }
- command
-}
-
-#[test]
-fn folder_without_node_files() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
-
- let output = render_node_module()
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = "";
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn folder_with_package_json() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("package.json"))?.sync_all()?;
-
- let output = render_node_module()
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"));
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn folder_with_js_file() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- File::create(dir.path().join("index.js"))?.sync_all()?;
-
- let output = render_node_module()
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"));
- assert_eq!(expected, actual);
- Ok(())
-}
-
-#[test]
-#[ignore]
-fn folder_with_node_modules() -> io::Result<()> {
- let dir = tempfile::tempdir()?;
- let node_modules = dir.path().join("node_modules");
- fs::create_dir_all(&node_modules)?;
-
- let output = render_node_module()
- .arg("--path")
- .arg(dir.path())
- .output()?;
- let actual = String::from_utf8(output.stdout).unwrap();
-
- let expected = format!("via {} ", Color::Green.bold().paint("⬢ v12.0.0"));
- assert_eq!(expected, actual);
- Ok(())
-}