Skip to content

Commit

Permalink
feat(jest): include testing scripts in jest setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Rickard Natt och Dag committed Mar 11, 2021
1 parent bc51eba commit 001922e
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 2 deletions.
23 changes: 22 additions & 1 deletion src/commands/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::utils::{
use colored::*;
use helpers::Result;
use serde_json::json;
use std::collections::HashMap;
use std::fs;

pub fn git(project_type: Option<ProjectType>) -> Result<()> {
Expand Down Expand Up @@ -70,7 +71,15 @@ pub fn jest() -> Result<()> {

spinner.set_message("Installing dependencies");

node::install_dev("jest jest-watch-typeahead");
node::install_dev("jest jest-watch-typeahead is-ci-cli");

let mut scripts = HashMap::new();

scripts.insert("test", "is-ci-cli test:ci test:watch");
scripts.insert("test:ci", "jest");
scripts.insert("test:watch", "jest --watch");

node::add_scripts(scripts)?;

template::render_file(
include_str!("../templates/jest.config.js"),
Expand All @@ -80,6 +89,18 @@ pub fn jest() -> Result<()> {

spinner.success("Jest setup complete");

println!(
"
New commands added
* {test} - Run tests in either CI mode or watch mode in dev
* {test_ci} - CI mode runs only if CI environment variable is set, uses is-ci-cli
* {test_watch} - Run tests in watch mode
",
test = "test".blue(),
test_ci = "test:ci".blue(),
test_watch = "test:watch".blue()
);

Ok(())
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ pub fn prettier() -> Result<()> {

pub fn jest() -> Result<()> {
fs::remove_file("jest.config.js")?;
node::uninstall("jest jest-watch-typeahead");
node::uninstall("jest jest-watch-typeahead is-ci-cli");
node::remove_scripts(vec!["test", "test:ci", "test:watch"])?;

Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub mod format;
pub mod helpers;
pub mod node;
pub mod progressbar;
pub mod pkg_json;
pub mod project;
pub mod template;
32 changes: 32 additions & 0 deletions src/utils/node.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use super::helpers;
use crate::config::{self, NodeInstaller};
use crate::utils::pkg_json;
use helpers::Result;
use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashMap;
use std::fs;

lazy_static! {
static ref PKG: Regex = Regex::new(r"([\w@/-]+)\{([\w\-,]+)\}").unwrap();
Expand Down Expand Up @@ -48,6 +52,34 @@ pub fn uninstall(pkg: &str) {
packages(pkg).iter().for_each(|p| uninstaller(p));
}

pub fn add_scripts(scripts: HashMap<&str, &str>) -> Result<()> {
let mut pkg = pkg_json::Package::new()?;

scripts.iter().for_each(|(name, cmd)| {
pkg.scripts.insert(name.to_string(), cmd.to_string());
});

let json = serde_json::to_string_pretty(&pkg)?;

fs::write("package.json", json)?;

Ok(())
}

pub fn remove_scripts(scripts: Vec<&str>) -> Result<()> {
let mut pkg = pkg_json::Package::new()?;

scripts.iter().for_each(|name| {
pkg.scripts.remove(&name.to_string());
});

let json = serde_json::to_string_pretty(&pkg)?;

fs::write("package.json", json)?;

Ok(())
}

fn split_packages(caps: regex::Captures) -> Option<Vec<String>> {
let base = caps.get(1)?.as_str();

Expand Down
201 changes: 201 additions & 0 deletions src/utils/pkg_json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Simplified version of npm-package-json
// https://github.com/SirWindfield/npm-package-json

use crate::utils::helpers;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::io::ErrorKind;
use std::{collections::BTreeMap, fs};

/// An ordered map for `bin` entries.
pub type BinSet = BTreeMap<String, String>;
/// An ordered map for `dependencies` entries.
pub type DepsSet = BTreeMap<String, String>;
/// An ordered map for `engines` entries.
pub type EnginesSet = BTreeMap<String, String>;
/// An ordered map for `scripts` entries.
pub type ScriptsSet = BTreeMap<String, String>;

/// The result type of this crate.
pub type Result<T> = std::result::Result<T, std::io::Error>;

/// A bug contacting form.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Bug {
/// The email to use for contact.
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
/// The url to use to submit bugs.
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}

/// A person.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Person {
/// The name of a person.
pub name: String,
/// The email of a person.
pub email: Option<String>,
/// The homepage of the person.
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}

/// A reference to a person.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum PersonReference {
/// A short reference.
///
/// Short references have a fixed format of `John Doe <john@doe.dev> (https://john.doe.dev)`.
Short(String),
/// A full reference.
///
/// This type of reference defines the parts using a struct instead of a
/// shorthand string format.
Full(Person),
}

/// A reference to a man page.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum ManReference {
/// A single man page reference. Points to one single file.
Single(String),
/// Multiple man pages, can contain anything from zero to n.
Multiple(Vec<String>),
}

/// A repository.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Repository {
/// The version control system that the repository uses.
pub r#type: String,
/// The url to the repository.
pub url: String,
/// The directory that the repository is in. Often used for monorepos.
#[serde(skip_serializing_if = "Option::is_none")]
pub directory: Option<String>,
}

/// A repository reference.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum RepositoryReference {
/// A short reference to the repository. Has to have the syntax that `npm install` allows as well. For more information see [here](https://docs.npmjs.com/files/package.json#repository).
Short(String),
/// A full reference.
Full(Repository),
}

/// The top-level `package.json` structure.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Package {
/// The package name.
pub name: String,
/// The package version.
pub version: String,
/// The optional package description.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// The optional package main entry file.
#[serde(skip_serializing_if = "Option::is_none")]
pub main: Option<String>,
/// The optional directories
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub directories: BTreeMap<String, String>,
/// The optional list of script entries.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub scripts: ScriptsSet,
/// The optional repository reference.
#[serde(skip_serializing_if = "Option::is_none")]
pub repository: Option<RepositoryReference>,
/// The optional list of keywords.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keywords: Vec<String>,
/// The optional author.
#[serde(skip_serializing_if = "Option::is_none")]
pub author: Option<PersonReference>,
/// The optional package license.
#[serde(skip_serializing_if = "Option::is_none")]
pub license: Option<String>,
/// The optional bug contact form.
#[serde(skip_serializing_if = "Option::is_none")]
pub bugs: Option<Bug>,
/// The optional package homepage.
#[serde(skip_serializing_if = "Option::is_none")]
pub homepage: Option<String>,
/// The optional list of contributors.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub contributors: Vec<PersonReference>,
/// The optional list of files to include. Each entry defines a regex
/// pattern.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub files: Vec<String>,
/// The optional package browser entry file.
///
/// This is usually defined in libraries that are meant to be consumed by
/// browsers. These Thoes can refer to objects that are not available inside
/// a `nodejs` environment (like `window`).
#[serde(skip_serializing_if = "Option::is_none")]
pub browser: Option<String>,
/// The optional set of binary definitions.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub bin: BinSet,
/// The optional list of man page references.
#[serde(skip_serializing_if = "Option::is_none")]
pub man: Option<ManReference>,
/// The optional list of dependencies.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dependencies: DepsSet,
/// The optional list of development dependencies.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub dev_dependencies: DepsSet,
/// The optional list of peer dependencies.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub peer_dependencies: DepsSet,
/// The optional list of bundled dependencies.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub bundled_dependencies: DepsSet,
/// The optional list of optional dependencies.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub optional_dependencies: DepsSet,
/// The optional list of engine entries.
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub engines: EnginesSet,
/// The package privacy.
#[serde(skip_serializing_if = "Option::is_none")]
pub private: Option<bool>,
/// The OS' that the package can run on.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub os: Vec<String>,
/// The CPU architectures that the package can run on.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub cpu: Vec<String>,
/// The optional config object.
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<Value>,
/// Other custom fields that have been defined inside the `package.json`
/// file.
#[serde(flatten)]
pub others: BTreeMap<String, Value>,
}

impl Package {
pub fn new() -> Result<Self> {
match fs::metadata("package.json") {
Ok(_) => (),
Err(err) => {
// Initialize an empty package.json if none exists
if let ErrorKind::NotFound = err.kind() {
helpers::run_command("npm", &["init", "-y"]);
}
}
};

let content = fs::read_to_string("package.json")?;
Ok(serde_json::from_str(&content)?)
}
}

0 comments on commit 001922e

Please sign in to comment.