Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Software Architecture

Riku Rauhala edited this page Feb 2, 2023 · 13 revisions

Software Architecture

In this document the overall software architecture and implementation are presented. This document can be useful for those wishing to contribute to the source code. For instructions on how to set up a local development environment and contribute to the project, please refer to the contribution guidelines.

Overview

0b.is is a web application, created with React and TypeScript. It is purely a frontend application as there is no need for any server-side functionality. Any calculations needed by the program are rather lightweight and can be handled by React.

The compiled application is hosted on GitHub Pages via the gh-pages package. A live version can be viewed at https://0b.is. It is possible to download the source code and run the application locally, but the recommended way is to navigate to the production version for the latest version, always available on the internet.

Functionality

Application logic can be found in the src/utils directory.

Validation

All numbers given as input are validated before they can be converted. Regular expressions are used to validate different number systems. For example, the following function checks hexadecimal number validity.

const validateHexadecimal = (hexadecimal: string): boolean => {
  const regex = new RegExp('^-?[0-9A-F]+$');
  return regex.test(hexadecimal.toUpperCase());
};

The regex matches whole strings with ^ marking the start and $ marking the end of a string. The minus sign is marked as optional: -?. The allowed characters are digits 0 to 9 and letters A to F (for numbers 10 to 15). One or more of the valid characters are expected: [0-9A-F]+.

Conversion

Number conversion is a more simple process as the same function is used for all conversions. The following function is used for converting numbers from base x to base y.

const convert = (number: string, input: NumeralSystem, output: NumeralSystem): string => {
  return parseInt(number, input).toString(output).toUpperCase();
};

User Interface

For usage instructions, see the user manual.

The application has a web-based user interface, created with React.js and Material UI components. The user interface is designed to be responsive, it works well on both mobile devices as well as on the desktop. The application supports both a light and a dark mode, the latter being the default option.

MenuBar

The MenuBar contains a button for toggling between the light and the dark theme. By default the theme used is the light theme but the application checks if the user prefers dark mode and in that case sets the mode to dark automatically. The button can be used to toggle between different themes.

The next button with the globe symbol opens a menu for choosing the language of the application. The last button with the question mark opens the user manual hosted on GitHub.

Converter

The main functionality is the converter itself. It consists of the input text field, where the user can type any number they wish to convert and the output where the converted number will appear. Both input and output also contain a selection of different numeral systems, such as the binary, decimal and hexadecimal systems. An error message will be displayed if the input number is invalid. The converted number can be copied to the clipboard.

InfoBoxes

Both input and output have corresponding info boxes below them. They are used to describe different numeral systems and their use cases. This way the application is not only useful but educational as well.

Footer

At the bottom of the page, a footer component can be found. It contains various useful links.

Continuous Integration

GitHub Actions is used for continuous integration. A configuration file defines the workflow as follows:

  • Set up Node
  • Install dependencies
  • Check the code style with ESLint
  • Generate a test coverage report
  • Upload the report to codecov.io

Testing

Unit tests have been created with React Testing Library. Testing all relevant logic and functionality is necessary so that the users can rely on the results produced by the application. Instead of having a specific directory for tests, each function or component has a corresponding test file located in the same directory. For example, the file validate.ts is tested in the file validate.test.ts.

Unit tests can be run with the following command.

$ CI=true npm test

Coverage

Test coverage is a useful metric to inspect the effectiveness of automated testing. A test coverage report can be generated with the following command.

$ CI=true npm test -- --coverage

The coverage report generated by the command can be viewed by opening the file coverage/lcov-report/index.html in the browser.

Documentation

All documentation can be found in the root of the repository or in the wiki.

JSDoc

Relevant functionality should be documented with JSDoc.

Here is an example from src/utils/convert.ts.

/**
 * Converts a number from base X to base Y.
 *
 * Supported systems:
 * - Base 2 (binary)
 * - Base 8 (octal)
 * - Base 10 (decimal)
 * - Base 16 (hexadecimal)
 * @param {string} number The number to be converted.
 * @param {NumeralSystem} input Input numeral system, base X.
 * @param {NumeralSystem} output Output numeral system, base Y.
 * @return {string} The converted number.
 */
const convert = (number: string, input: NumeralSystem, output: NumeralSystem): string => {
  return parseInt(number, input).toString(output).toUpperCase();
};

Project Structure

The project has the following structure. Notice that some of the files and directories are ignored by the version control and will only appear locally after running scripts such as build, test or lint. The structure should stay as logical as possible so if you are to contribute to the source code and make any new functionality, please refer to the structure and find a suitable location for any new files you may create.

├── .github                                 # GitHub related files and configs
│   ├── ISSUE_TEMPLATE                      #
│   │   └── ...                             #
│   ├── workflows                           #
│   │   └── main.yml                        # continuous integration config
│   ├── CODE_OF_CONDUCT.md                  #
│   ├── CONTRIBUTING.md                     # contribution guidelines
│   ├── dependabot.yml                      # Dependabot config
│   └── SECURITY.md                         #
├── build                                   # compiled source code
│   └── ...                                 #
├── coverage                                #
│   ├── ...                                 #
│   └── lcov-report                         #
│       ├── ...                             #
│       ├── index.html                      # test coverage report
│       └── ...                             #
├── node_modules                            # dependencies
│   └── ...                                 #
├── public                                  # files such as index.html, favicon etc.
│   └── ...                                 #
├── src                                     # source code
│   ├── components                          # React components
│   │   ├── Footer                          #
│   │   │   ├── FooterButton                #
│   │   │   │   ├── FooterButton.tsx        #
│   │   │   │   └── index.ts                #
│   │   │   ├── Footer.tsx                  #
│   │   │   └── index.ts                    #
│   │   ├── InfoBox                         #
│   │   │   ├── content                     # info box content files
│   │   │   │   ├── 2                       # binary
│   │   │   │   │   ├── ...                 # other languages
│   │   │   │   │   ├── en.md               # original English text
│   │   │   │   │   └── ...                 # other languages
│   │   │   │   ├── 8                       # octal
│   │   │   │   │   ├── ...                 #
│   │   │   │   │   ├── en.md               #
│   │   │   │   │   └── ...                 #
│   │   │   │   ├── 10                      # decimal
│   │   │   │   │   ├── ...                 #
│   │   │   │   │   ├── en.md               #
│   │   │   │   │   └── ...                 #
│   │   │   │   └── 16                      # hexadecimal
│   │   │   │       ├── ...                 #
│   │   │   │       ├── en.md               #
│   │   │   │       └── ...                 #
│   │   │   ├── InfoBoxContent              #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── InfoBoxContent.tsx      #
│   │   │   ├── ReadMoreButton              #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── ReadMoreButton.tsx      #
│   │   │   ├── index.ts                    #
│   │   │   └── InfoBox.tsx                 #
│   │   ├── MenuBar                         #
│   │   │   ├── AppName                     #
│   │   │   │   ├── AppName.tsx             #
│   │   │   │   └── index.ts                #
│   │   │   ├── LanguageButton              #
│   │   │   │   ├── LanguageOption          #
│   │   │   │   │   ├── index.ts            #
│   │   │   │   │   └── LanguageOption.tsx  #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── LanguageButton.tsx      #
│   │   │   ├── LogoPlaceholder             #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── LogoPlaceholder.tsx     #
│   │   │   ├── ModeToggleButton            #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── ModeToggleButton.tsx    #
│   │   │   ├── UserManualButton            #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── UserManualButton.tsx    #
│   │   │   ├── index.ts                    #
│   │   │   └── MenuBar.tsx                 #
│   │   ├── NumberInput                     #
│   │   │   ├── InputSystemSelect           #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── InputSystemSelect.tsx   #
│   │   │   ├── InputTextField              #
│   │   │   │   ├── index.ts                #
│   │   │   │   └── InputTextField.tsx      #
│   │   │   ├── index.ts                    #
│   │   │   ├── NumberInput.test.tsx        #
│   │   │   └── NumberInput.tsx             #
│   │   └── NumberOutput                    #
│   │       ├── OutputSystemSelect          #
│   │       │   ├── index.ts                #
│   │       │   └── OutputSystemSelect.tsx  #
│   │       ├── OutputTextField             #
│   │       │   ├── CopyButton              #
│   │       │   │   ├── CopyButton.tsx      #
│   │       │   │   └── index.ts            #
│   │       │   ├── index.ts                #
│   │       │   └── OutputTextField.tsx     #
│   │       ├── index.ts                    #
│   │       └── NumberOutput.tsx            #
│   ├── languages                           # translations
│   │   ├── index.ts                        #
│   │   ├── links.json                      #
│   │   └── translations.json               #
│   ├── themes                              # theme definitions 
│   │   └── index.ts                        #
│   ├── types                               # TypeScript type definitions
│   │   ├── index.ts                        #
│   │   └── markdown.d.ts                   #
│   └── utils                               # utility functions, application log
│   │   ├── convert.test.ts                 #
│   │   ├── convert.ts                      #
│   │   ├── systems.test.ts                 #
│   │   ├── systems.ts                      #
│   │   ├── testInputs.ts                   #
│   │   ├── validate.test.ts                #
│   │   └── validate.ts                     #
│   ├── App.tsx                             # main component
│   ├── index.tsx                           # entry point
│   ├── meta.tsx                            # dynamically changing the page title
│   ├── react-app-env.d.ts                  #
│   └── setupTests.ts                       #
├── .eslintrc                               # ESLint config
├── .gitignore                              #
├── LICENSE.md                              #
├── package-lock.json                       #
├── package.json                            # 
├── README.md                               #
└── tsconfig.json                           # TypeScript config