A Node.js CLI application that will take in information about employees on a software engineering team, then generates an HTML webpage that displays summaries for each person, following Agile methodology and TDD development process.
- Test-Driven Development: Team Profile Generator
In this Challenge, you'll create an application that generates HTML files using input from a command-line interface.
Your task is to take the given starter code and convert it into a working Node.js command-line application. This application will take in information about employees on a software engineering team, then generate an HTML webpage that displays summaries for each person. You have been provided with tests, so make sure every part of your code passes each provided test.
Your application should use JestLinks to an external site. for running unit tests and InquirerLinks to an external site. for collecting input from the user. The application will be invoked by using the following command:
node index.mjs
👆 Please note the
index.mjs
extension above. This is because the project has been developed using ES6 modules and no CommonJS syntax.
Click on each screenshot to take you to the Walkthrough video, a Team Profile's live example, and an example in PDF version.
Walkthrough Video https://user-images.githubusercontent.com/2573750/222523833-77af7fda-d324-4848-b360-2ecda53db25e.mp4
- Solution URL: https://github.com/technoveltyco/bootcamp-week12-challenge
- Live Site URL: https://technoveltyco.github.io/bootcamp-week12-challenge/
- KANBAN Dashboard: https://github.com/users/technoveltyco/projects/6
I started designing the structure of the program by creating a Flowchart Diagram, Class Diagram and Sequence Diagram, so I could have a reference of the logic structure, modules and overall idea of the automated tests coverage required. Finally, I went through the ideas and doing investigation for the UX/UI Design, starting from the terminal theming, and following with the webpage design creating a visualisation on screens and a printer-friendly version.
- Semantic HTML5 markup
- CSS custom properties
- Flexbox
- CSS Grid
- Mobile-first workflow
- Boostrap 4
- Node.js
- Mermaid
---
title: Team Profile Generator
---
flowchart TB
A((start)) --> B[Create new Team]
B --> C[/Prompt Team Manager/]
C --> D{Input data \nvalidates?}
D -->|No| C[/Prompt Team Manager/]
D -->|Yes| E[Add new Employee to Team]
E --> F[/Prompt Main Menu/]
F --> G{Add an Engineer?} & H{Add an Intern?} & I{Finish the Team?}
G -->|No| F
G -->|Yes| J[/Prompt Engineer/]
J --> D
H -->|No| F
H -->|Yes| K[/Prompt Intern/]
K --> D
I -->|No| F
I -->|Yes| L[Create HTML]
L --> M((Finish))
---
title: Team Profile Generator
---
classDiagram
AppSingleton <.. ClientConsole
AppSingleton "1" <-- "1" EmployeeFactory : contains
AppSingleton "1" <-- "1" TeamComposite : contains
Team <|-- TeamComposite
Team "0..*" <--* "1" TeamComposite
Employee <|-- Team
Employee "0..*" <--* "1" Team
Employee <|-- Manager
Employee <|-- Engineer
Employee <|-- Intern
class AppSingleton {
-TeamComposite teamsInstance
-AppSIngleton():void
+getInstance()$:AppSingleton
}
class EmployeeFactory {
+createEmployee(String type, Object params)*:Employee
}
class TeamComposite {
-EmployeeFactory factory
-List~Team~ teams
+getTeams():List~Team~
+getTeam(Integer index):Team
+addTeam(Team team):void
+removeTeam():Team
+clearTeams():void
}
class Team {
-List~Employee~ employees
+getEmployees():List~Employee~
+getEmployee(Integer id):Employee
+addEmployee(Employee employee):void
+removeEmployee(Integer id):Employee
+clearEmployees():void
}
class Employee {
<<abstract>>
-String name
-Integer id
-String email
+createEmployee(Object params):Employee
+getName():String
+getId():Integer
+getEmail():String
+getRole()*:String
}
class Manager {
-Integer officeNumber
+createEmployee(Object params):Employee
+getOfficeNumber:Integer
+getRole():String ~~override~~
}
class Engineer {
-String github
+createEmployee(Object params):Employee
+getGithub():String
+getRole():String ~~override~~
}
class Intern {
-String school
+createEmployee(Object params):Employee
+getSchool():String
+getRole():String ~~override~~
}
I extended the original models to include a more scalable architecture using the following design patterns:
- Singleton: the App class implements this pattern to restrict the creation of more than one instance in our application. This is the main entry point in the application flow, and handles more of the errors and recovery states of the program flow.
- Factory: due to JavaScript limitations for creating abstract classes and interfaces, the implementation of classical OO factory patterns are usually achieved by using functional patterns. However, a mixed implementation between the Abstract factory pattern and Factory method pattern was used to achieve flexibility in the design and allow the creation of model classes using 3 different approaches:
///
// Examples of different ways to initialise employees/manager/engineer/intern.
// ------------------------------------------------------------------------------
// - With constructor:
// const employee = new Employee(...data);
// - With factory method:
// const employee = Employee.createEmployee(...data);
// - With factory abstract class method: (the one used here)
// const employee = EmployeeFactory.createEmployee("manager", ...data);
- Composite: used for the container classes that store multiple instances of the model object, and allowing to create and control higher level data structures like: team of employees, and list of teams. By adding these structures, it also required to extend the test suite to check the correct integration of the data structures and ensure that the desired access restrictions to the data would be accomplished.
sequenceDiagram
autonumber
activate User
User->>App:Start
activate App
App->>Inquirer:prompt Team Manager
activate Inquirer
loop Name, ID, email, office
Inquirer-->>User:request manager data
User->>Inquirer:input
Inquirer->>Inquirer:Validate input
Inquirer-->>User:output result
end
Inquirer-->>App:Name, ID, email, office
deactivate Inquirer
App->>Team:create
activate Team
Team-->>App:<<Team>>
App->>Manager:Name,ID,email,office
activate Manager
Manager-->>App:<<Manager>>
deactivate Manager
App->>Team:add <<Employee>>
deactivate Team
App->>Inquirer:prompt Main Menu
activate Inquirer
loop is not stop prompt
Inquirer-->>User:request employee data
User->>Inquirer:chosen option
alt option is Add an engineer
loop name,ID,email,github
Inquirer-->>User:request employee data
User->>Inquirer:input
Inquirer->>Inquirer:Validate input
Inquirer-->>User:output result
Inquirer-->>App:Name,ID,email,github
App->>Engineer:Name,ID,email,github
activate Engineer
Engineer-->App:<<Engineer>>
deactivate Engineer
end
App->>Inquirer:prompt Main Menu
else alt option is Add an Intern
loop name,ID,email,school
Inquirer-->>User:request employee data
User->>Inquirer:input
Inquirer->>Inquirer:Validate input
Inquirer-->>User:output result
Inquirer-->>App:Name,ID,email,school
App->>Intern:Name,ID,email,school
activate Intern
Intern-->>App:<<Intern>>
deactivate Intern
end
App->>Team:add employee <<Intern>>
App->>Inquirer:prompt Main Menu
else option is Finish building the team
Inquirer-->>App: stop prompt
deactivate Inquirer
end
end
App->>HTML Generator:generate
HTML Generator-->>App:<<DOM>>
App-->>User:<<HTML>>
deactivate App
deactivate User
A definition of colors and formatting of the output were created in a specific CLI module providing different utilities and helper functions.
Emoji | Text Color | Background Color | Text decoration | Behaviour | Terminal UI |
---|---|---|---|---|---|
✔️ | #008000 |
#000000 |
-- | Success | -- |
✔️ | #000000 |
#00ffff |
-- | Done | -- |
#ffa500 |
#000000 |
-- | Warning | -- | |
-- | #ffa500 |
#000000 |
-- | Fail validate | -- |
#000000 |
#00ffff |
-- | Info | Bottom Bar | |
❌ | #ff0000 |
#000000 |
-- | Fatal Error | Bottom Bar |
📂 | #ffffff |
#000000 |
-- | Created | -- |
🙏 | #ffffff |
#000000 |
-- | Thanks | -- |
-- | #ffffff |
#000000 |
Bold | Label | -- |
-- | #000000 |
#00ffff |
-- | Highlight | -- |
#000000 |
#00ffff |
-- | Notify | Bottom Bar | |
📛 | #ffffff |
#000000 |
Bold | Name Input Label | -- |
🆔 | #ffffff |
#000000 |
Bold | ID Input Label | -- |
📧 | #ffffff |
#000000 |
Bold | Email Input Label | -- |
🏢 | #ffffff |
#000000 |
Bold | Office Number Input Label | -- |
💻 | #ffffff |
#000000 |
Bold | GitHub Account Input Label | -- |
🏫 | #ffffff |
#000000 |
Bold | School Input Label | -- |
-- | #ffffff |
#000000 |
-- | Default text | -- |
This cover intro was created but still not implemented in v1.0.
███████████ ███████████ ██████ ███ ████ █████████ █████
░█░░░███░░░█ ░░███░░░░░███ ███░░███ ░░░ ░░███ ███░░░░░███ ░░███
░ ░███ ░ ██████ ██████ █████████████ ░███ ░███ ████████ ██████ ░███ ░░░ ████ ░███ ██████ ███ ░░░ ██████ ████████ ██████ ████████ ██████ ███████ ██████ ████████
░███ ███░░███ ░░░░░███ ░░███░░███░░███ ░██████████ ░░███░░███ ███░░███ ███████ ░░███ ░███ ███░░███ ░███ ███░░███░░███░░███ ███░░███░░███░░███ ░░░░░███ ░░░███░ ███░░███░░███░░███
░███ ░███████ ███████ ░███ ░███ ░███ ░███░░░░░░ ░███ ░░░ ░███ ░███░░░███░ ░███ ░███ ░███████ ░███ █████░███████ ░███ ░███ ░███████ ░███ ░░░ ███████ ░███ ░███ ░███ ░███ ░░░
░███ ░███░░░ ███░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░ ░░███ ░░███ ░███░░░ ░███ ░███ ░███░░░ ░███ ███░░███ ░███ ███░███ ░███ ░███
█████ ░░██████ ░░████████ █████░███ █████ █████ █████ ░░██████ █████ █████ █████░░██████ ░░█████████ ░░██████ ████ █████░░██████ █████ ░░████████ ░░█████ ░░██████ █████
░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░░░
, , █████
/ \/ \,'| _ ░░███
,' ' ,' |,| ░███████ █████ ████
,' ' |,'| ░███░░███░░███ ░███
,' ;'| _ ░███ ░███ ░███ ░███
,' '' | ░███ ░███ ░███ ░███
,' ;-, ████████ ░░███████
(___ / ░░░░░░░░ ░░░░░███
,' `. ___ ,' ███ ░███
: ,`' `-. / ░░██████
|-._ o / \ / ░░░░░░
( `-( ) /
,'`. \ o / ,'
/ ` `. ,' /
( `"""' / ███████████ █████ ████ █████
`._ / ░█░░░███░░░█ ░░███ ░░███ ░░███
`--.______ '"`. ░ ░███ ░ ██████ ██████ ░███████ ████████ ██████ █████ █████ ██████ ░███ ███████ █████ ████
\__,__,`---._ '`; ░███ ███░░███ ███░░███ ░███░░███ ░░███░░███ ███░░███░░███ ░░███ ███░░███ ░███ ░░░███░ ░░███ ░███
))`-^--')`,-' ░███ ░███████ ░███ ░░░ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░███ ░███ ░███ ░███
,',_____,' | ░███ ░███░░░ ░███ ███ ░███ ░███ ░███ ░███ ░███ ░███ ░░███ ███ ░███░░░ ░███ ░███ ███ ░███ ░███
\_ `). █████ ░░██████ ░░██████ ████ █████ ████ █████░░██████ ░░█████ ░░██████ █████ ░░█████ ░░███████
`. _,' ` ░░░░░ ░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░░███
/`-._,-' \ ███ ░███
░░██████
░░░░░░
The theming was inpired in the idea of practising UI design shapes using CSS effects and vectorised images in SVG format. In addition for usability considerations, a printer-friendly theme was created too.
To look at the sample of a generated Team Profile follow the links below:
- HTML format
- PDF format (printer-friendly version)
Tests were created for all the models implemented in the diagram of classes, covering ~100% of the code.
You can run the tests from the project root folder using npm run test
.
Use this section to recap over some of your major learnings while working through this project. Writing these out and providing code samples of areas you want to highlight is a great way to reinforce your own knowledge.
To see how you can add code snippets, see below:
<h1>Some HTML code I'm proud of</h1>
.proud-of-this-css {
color: papayawhip;
}
const proudOfThisFunc = () => {
console.log("🎉");
};
The following tickets has been created for future development:
- Bug fixes & Improvements
- Functional tests for the document generation.
- Remote Terminal & Security implications
- Landing page
- Docs page
- JavaScript
- MDN
- Classes
- super
- Private class features
- Simulating private constructors - This section of the document was particularly helpful to implement the Singleton pattern non being constructable.
- static
- Object
- Working with objects
- Iterators and generators
- Symbol.iterator - implemented in composite to handle the list of object containers.
- Iteration protocols
- Optional chaining (?.)
- RegExp
- Object-oriented JavaScript: A Deep Dive into ES6 Classes
- Classes in JS: Public, Private and Protected
- Quick Guide to using interfaces with JavaScript
- Interfaces in JavaScript with ES6 Symbol. Naive implementation
- Factory Design Pattern
- JavaScript Object Oriented Patterns: Factory Pattern
- JS ES6 Design Patterns: Factory
- JavaScript Factory Method
- Class, Factory, and Object Prototypes in JavaScript
- Class vs Factory function: exploring the way forward
- 2 Ways to Implement Abstract Factory Pattern in JavaScript
- 4 Ways to Implement Factory Pattern in JavaScript
- The 7 Most Useful Design Patterns in ES6 (and how you can implement them)
- JavaScript Design Patterns: The Singleton
- How to Implement the Singleton Pattern in JavaScript ES6+
- Singletons in ES6 - The Good, The Bad, The Ugly
- Singletons in JavaScript
- How to Implement the Singleton Pattern in JavaScript ES6 +
- Singleton Pattern in ES6 and ES7
- Named function parameters in ES6
- Deep Equality checking of Objects in Vanilla JavaScript
- Object references and copying
- Demystifying ES6 Iterables & Iterators
- JavaScript - Creating Custom Iterable by applying ES6 Iterable and Iterator Protocols
- 9 Ways to Remove Elements From A JavaScript Array - Plus How to Safely Clear JavaScript Arrays
- A Practical guide to ES6 modules
- Email Validation: Regex & JavaScript
- MDN
- TDD
- Jest
- A Beginner's Guide to Unit-testing with Jest
- Unit Testing with Jest
- How to write unit tests in JavaScript with Jest
- How to test classes with Jest
- Write Tests That Tell a Story
- Unit Testing of React Apps using JEST : Tutorial
- Black-Box Testing: The Category-Partition Method
- Software Development Process: Part 3 of 3
- Category Partition Demo - Georgia Tech - Software Development Process
- 11 API Testing Interview Questions (and Answers) to Help You Prepare For Your Next Interview
- Debugging: the biggest waste
- Node.js
- How to fix "__dirname is not defined in ES module scope"
- ReferenceError: __dirname is not defined in ES module scope
- Node.js File Paths
- Modules: ECMAScript modules
- Determining module system
- ascii-themes - Node.js CLI Interface to Generate VSCode Themed ASCII Art
- Animate the text in your terminal
- Using console colors with Node.js
- How do I clear the console in node JS?
- UX/UI
- GitHub
- Other
- SysML Open Source Project: What is SysML? Who created SysML?
- Emojipedia
- ASCII text art generator
- ASCII Art Archive
- ascii.co.uk
- asciinema.org
- Wikipedia - Email address
- Mail::RFC822::Address: regexp-based address validation
- The SOLID Principles of Object-Oriented Programming Explained in Plain English
- Anti-patterns You Should Avoid in Your Code
Daniel Rodriguez
- Website - Technovelty.co
- GitHub - Technovelty
The teacher and TAs that help us with resources and support to my questions during the development of this project.