feature/upgrade #12
					 96 changed files with 13140 additions and 11184 deletions
				
			
		
							
								
								
									
										12
									
								
								.drone.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.drone.yml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | kind: pipeline | ||||||
|  | type: docker | ||||||
|  | name: default | ||||||
|  | 
 | ||||||
|  | steps: | ||||||
|  |   - name: install | ||||||
|  |     image: node:20-alpine | ||||||
|  |     commands: | ||||||
|  |       - apk add firefox | ||||||
|  |       - npm install | ||||||
|  |       - npm run test:ci | ||||||
|  |       - npm run build | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| # Editor configuration, see http://editorconfig.org | # Editor configuration, see https://editorconfig.org | ||||||
| root = true | root = true | ||||||
| 
 | 
 | ||||||
| [*] | [*] | ||||||
|  | @ -8,6 +8,9 @@ indent_size = 2 | ||||||
| insert_final_newline = true | insert_final_newline = true | ||||||
| trim_trailing_whitespace = true | trim_trailing_whitespace = true | ||||||
| 
 | 
 | ||||||
|  | [*.ts] | ||||||
|  | quote_type = single | ||||||
|  | 
 | ||||||
| [*.md] | [*.md] | ||||||
| max_line_length = off | max_line_length = off | ||||||
| trim_trailing_whitespace = false | trim_trailing_whitespace = false | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,44 +1,42 @@ | ||||||
| # See http://help.github.com/ignore-files/ for more about ignoring files. | # See http://help.github.com/ignore-files/ for more about ignoring files. | ||||||
| 
 | 
 | ||||||
| # compiled output | # Compiled output | ||||||
| /dist | /dist | ||||||
| /tmp | /tmp | ||||||
| /out-tsc | /out-tsc | ||||||
|  | /bazel-out | ||||||
| 
 | 
 | ||||||
| # dependencies | # Node | ||||||
| /node_modules | /node_modules | ||||||
|  | npm-debug.log | ||||||
|  | yarn-error.log | ||||||
| 
 | 
 | ||||||
| # IDEs and editors | # IDEs and editors | ||||||
| /.idea | .idea/ | ||||||
| .project | .project | ||||||
| .classpath | .classpath | ||||||
| .c9/ | .c9/ | ||||||
| *.launch | *.launch | ||||||
| .settings/ | .settings/ | ||||||
| *.sublime-workspace | *.sublime-workspace | ||||||
| *.iml |  | ||||||
| 
 | 
 | ||||||
| # IDE - VSCode | # Visual Studio Code | ||||||
| .vscode/* | .vscode/tasks.json | ||||||
| !.vscode/settings.json | !.vscode/settings.json | ||||||
| !.vscode/tasks.json | !.vscode/tasks.json | ||||||
| !.vscode/launch.json | !.vscode/launch.json | ||||||
| !.vscode/extensions.json | !.vscode/extensions.json | ||||||
|  | .history/* | ||||||
| 
 | 
 | ||||||
| # misc | # Miscellaneous | ||||||
| /.sass-cache | /.angular/cache | ||||||
|  | .sass-cache/ | ||||||
| /connect.lock | /connect.lock | ||||||
| /coverage | /coverage | ||||||
| /libpeerconnection.log | /libpeerconnection.log | ||||||
| npm-debug.log |  | ||||||
| testem.log | testem.log | ||||||
| /typings | /typings | ||||||
| 
 | 
 | ||||||
| # e2e | # System files | ||||||
| /e2e/*.js |  | ||||||
| /e2e/*.map |  | ||||||
| 
 |  | ||||||
| # System Files |  | ||||||
| .DS_Store | .DS_Store | ||||||
| Thumbs.db | Thumbs.db | ||||||
| *~ |  | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,8 +1,8 @@ | ||||||
| # convertorizr - Convert whatever you want! | # Convertorizr - Convert whatever you want! | ||||||
| 
 | 
 | ||||||
| This is a short introduction to the awesome Convertorizr hosted at https://conv.friedli.info/. | This is a short introduction to the awesome Convertorizr hosted at https://conv.friedli.info/. | ||||||
| 
 | 
 | ||||||
| Deployment is automated with Gitlab CI. Usage is self-explanatory. What else do you need to know? | Continuous integration is automated with Drone CI ([](https://ci.gittr.ch/manuel/converter)). Usage is self-explanatory. What else do you need to know? | ||||||
| 
 | 
 | ||||||
| The source code is hosted at https://gittr.ch/manuel/converter.git. | The source code is hosted at https://gittr.ch/manuel/converter.git. | ||||||
| Contact the author at manuel-convertorizr|at|fritteli.ch. | Contact the author at manuel-convertorizr|at|fritteli.ch. | ||||||
|  | @ -11,11 +11,11 @@ Cheers! | ||||||
| 
 | 
 | ||||||
| # Technical stuff | # Technical stuff | ||||||
| 
 | 
 | ||||||
| This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.1.5. | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.1.0. | ||||||
| 
 | 
 | ||||||
| ## Development server | ## Development server | ||||||
| 
 | 
 | ||||||
| Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. | ||||||
| 
 | 
 | ||||||
| ## Code scaffolding | ## Code scaffolding | ||||||
| 
 | 
 | ||||||
|  | @ -23,7 +23,7 @@ Run `ng generate component component-name` to generate a new component. You can | ||||||
| 
 | 
 | ||||||
| ## Build | ## Build | ||||||
| 
 | 
 | ||||||
| Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. | ||||||
| 
 | 
 | ||||||
| ## Running unit tests | ## Running unit tests | ||||||
| 
 | 
 | ||||||
|  | @ -31,8 +31,8 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. | ||||||
| 
 | 
 | ||||||
| ## Running end-to-end tests | ## Running end-to-end tests | ||||||
| 
 | 
 | ||||||
| Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. | ||||||
| 
 | 
 | ||||||
| ## Further help | ## Further help | ||||||
| 
 | 
 | ||||||
| To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. | ||||||
|  |  | ||||||
							
								
								
									
										127
									
								
								angular.json
									
										
									
									
									
								
							
							
						
						
									
										127
									
								
								angular.json
									
										
									
									
									
								
							|  | @ -3,21 +3,28 @@ | ||||||
|   "version": 1, |   "version": 1, | ||||||
|   "newProjectRoot": "projects", |   "newProjectRoot": "projects", | ||||||
|   "projects": { |   "projects": { | ||||||
|     "converter": { |     "convertorizr": { | ||||||
|  |       "projectType": "application", | ||||||
|  |       "schematics": { | ||||||
|  |         "@schematics/angular:component": { | ||||||
|  |           "style": "scss" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|       "root": "", |       "root": "", | ||||||
|       "sourceRoot": "src", |       "sourceRoot": "src", | ||||||
|       "projectType": "application", |  | ||||||
|       "prefix": "app", |       "prefix": "app", | ||||||
|       "schematics": {}, |  | ||||||
|       "architect": { |       "architect": { | ||||||
|         "build": { |         "build": { | ||||||
|           "builder": "@angular-devkit/build-angular:browser", |           "builder": "@angular-devkit/build-angular:application", | ||||||
|           "options": { |           "options": { | ||||||
|             "outputPath": "dist/converter", |             "outputPath": "dist/convertorizr", | ||||||
|             "index": "src/index.html", |             "index": "src/index.html", | ||||||
|             "main": "src/main.ts", |             "browser": "src/main.ts", | ||||||
|             "polyfills": "src/polyfills.ts", |             "polyfills": [ | ||||||
|             "tsConfig": "src/tsconfig.app.json", |               "zone.js" | ||||||
|  |             ], | ||||||
|  |             "tsConfig": "tsconfig.app.json", | ||||||
|  |             "inlineStyleLanguage": "scss", | ||||||
|             "assets": [ |             "assets": [ | ||||||
|               "src/favicon.ico", |               "src/favicon.ico", | ||||||
|               "src/assets" |               "src/assets" | ||||||
|  | @ -29,99 +36,67 @@ | ||||||
|           }, |           }, | ||||||
|           "configurations": { |           "configurations": { | ||||||
|             "production": { |             "production": { | ||||||
|               "fileReplacements": [ |               "budgets": [ | ||||||
|                 { |                 { | ||||||
|                   "replace": "src/environments/environment.ts", |                   "type": "initial", | ||||||
|                   "with": "src/environments/environment.prod.ts" |                   "maximumWarning": "500kb", | ||||||
|  |                   "maximumError": "1mb" | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                   "type": "anyComponentStyle", | ||||||
|  |                   "maximumWarning": "2kb", | ||||||
|  |                   "maximumError": "4kb" | ||||||
|                 } |                 } | ||||||
|               ], |               ], | ||||||
|               "optimization": true, |               "outputHashing": "all" | ||||||
|               "outputHashing": "all", |             }, | ||||||
|               "sourceMap": false, |             "development": { | ||||||
|               "extractCss": true, |               "optimization": false, | ||||||
|               "namedChunks": false, |               "extractLicenses": false, | ||||||
|               "aot": true, |               "sourceMap": true | ||||||
|               "extractLicenses": true, |  | ||||||
|               "vendorChunk": false, |  | ||||||
|               "buildOptimizer": true |  | ||||||
|             } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|  |           "defaultConfiguration": "production" | ||||||
|  |         }, | ||||||
|         "serve": { |         "serve": { | ||||||
|           "builder": "@angular-devkit/build-angular:dev-server", |           "builder": "@angular-devkit/build-angular:dev-server", | ||||||
|           "options": { |  | ||||||
|             "browserTarget": "converter:build" |  | ||||||
|           }, |  | ||||||
|           "configurations": { |           "configurations": { | ||||||
|             "production": { |             "production": { | ||||||
|               "browserTarget": "converter:build:production" |               "buildTarget": "convertorizr:build:production" | ||||||
|             } |             }, | ||||||
|  |             "development": { | ||||||
|  |               "buildTarget": "convertorizr:build:development" | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|  |           "defaultConfiguration": "development" | ||||||
|  |         }, | ||||||
|         "extract-i18n": { |         "extract-i18n": { | ||||||
|           "builder": "@angular-devkit/build-angular:extract-i18n", |           "builder": "@angular-devkit/build-angular:extract-i18n", | ||||||
|           "options": { |           "options": { | ||||||
|             "browserTarget": "converter:build" |             "buildTarget": "convertorizr:build" | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "test": { |         "test": { | ||||||
|           "builder": "@angular-devkit/build-angular:karma", |           "builder": "@angular-devkit/build-angular:karma", | ||||||
|           "options": { |           "options": { | ||||||
|             "main": "src/test.ts", |             "polyfills": [ | ||||||
|             "polyfills": "src/polyfills.ts", |               "zone.js", | ||||||
|             "tsConfig": "src/tsconfig.spec.json", |               "zone.js/testing" | ||||||
|             "karmaConfig": "src/karma.conf.js", |             ], | ||||||
|  |             "tsConfig": "tsconfig.spec.json", | ||||||
|  |             "inlineStyleLanguage": "scss", | ||||||
|  |             "assets": [ | ||||||
|  |               "src/favicon.ico", | ||||||
|  |               "src/assets" | ||||||
|  |             ], | ||||||
|             "styles": [ |             "styles": [ | ||||||
|               "src/styles.scss" |               "src/styles.scss" | ||||||
|             ], |             ], | ||||||
|             "scripts": [], |             "scripts": [], | ||||||
|             "assets": [ |             "karmaConfig": "karma.conf.js" | ||||||
|               "src/favicon.ico", |           } | ||||||
|               "src/assets" |  | ||||||
|             ] |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         "lint": { |  | ||||||
|           "builder": "@angular-devkit/build-angular:tslint", |  | ||||||
|           "options": { |  | ||||||
|             "tsConfig": [ |  | ||||||
|               "src/tsconfig.app.json", |  | ||||||
|               "src/tsconfig.spec.json" |  | ||||||
|             ], |  | ||||||
|             "exclude": [ |  | ||||||
|               "**/node_modules/**" |  | ||||||
|             ] |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "converter-e2e": { |  | ||||||
|       "root": "e2e/", |  | ||||||
|       "projectType": "application", |  | ||||||
|       "architect": { |  | ||||||
|         "e2e": { |  | ||||||
|           "builder": "@angular-devkit/build-angular:protractor", |  | ||||||
|           "options": { |  | ||||||
|             "protractorConfig": "e2e/protractor.conf.js", |  | ||||||
|             "devServerTarget": "converter:serve" |  | ||||||
|           }, |  | ||||||
|           "configurations": { |  | ||||||
|             "production": { |  | ||||||
|               "devServerTarget": "converter:serve:production" |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         }, |  | ||||||
|         "lint": { |  | ||||||
|           "builder": "@angular-devkit/build-angular:tslint", |  | ||||||
|           "options": { |  | ||||||
|             "tsConfig": "e2e/tsconfig.e2e.json", |  | ||||||
|             "exclude": [ |  | ||||||
|               "**/node_modules/**" |  | ||||||
|             ] |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   }, |  | ||||||
|   "defaultProject": "converter" |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| import {ConvertorizrPage} from './app.po'; |  | ||||||
| 
 |  | ||||||
| describe('convertorizr App', () => { |  | ||||||
|   let page: ConvertorizrPage; |  | ||||||
| 
 |  | ||||||
|   beforeEach(() => { |  | ||||||
|     page = new ConvertorizrPage(); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should display a textarea that is initially empty', () => { |  | ||||||
|     page.navigateTo() |  | ||||||
|       .then(() => page.getInputFieldContent(0)) |  | ||||||
|       .then((value: string) => { |  | ||||||
|         expect(value).toEqual(''); |  | ||||||
|       }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should convert a string to its base64 representation', () => { |  | ||||||
|     page.navigateTo() |  | ||||||
|       .then(() => page.setInputFieldContent(0, 'Hello, World!')) |  | ||||||
|       .then(() => page.getSelectedConverterOption(0)) |  | ||||||
|       .then((option: string) => { |  | ||||||
|         expect(option).toEqual('Select conversion ...'); |  | ||||||
|       }) |  | ||||||
|       .then(() => page.selectConverterOption(0, 'Encode Base 64')) |  | ||||||
|       .then(() => page.getInputFieldContent(1)) |  | ||||||
|       .then((content: string) => { |  | ||||||
|         expect(content).toEqual('SGVsbG8sIFdvcmxkIQ=='); |  | ||||||
|       }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  | @ -1,44 +0,0 @@ | ||||||
| import {browser, by, element, ElementFinder} from 'protractor'; |  | ||||||
| import {promise, WebElementPromise} from 'selenium-webdriver'; |  | ||||||
| 
 |  | ||||||
| export class ConvertorizrPage { |  | ||||||
|   navigateTo(): promise.Promise<any> { |  | ||||||
|     return browser.get('/'); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private getInputField(index: number): WebElementPromise { |  | ||||||
|     return element |  | ||||||
|       .all(by.css('app-root div.inputwrapper')) |  | ||||||
|       .get(index) |  | ||||||
|       .element(by.css('.textwrapper textarea')) |  | ||||||
|       .getWebElement(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getInputFieldContent(index: number): promise.Promise<string> { |  | ||||||
|     return this.getInputField(index).getText(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   setInputFieldContent(index: number, content: string): promise.Promise<void> { |  | ||||||
|     return this.getInputField(index).sendKeys(content); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   private getConverterDropdown(index: number): ElementFinder { |  | ||||||
|     return element |  | ||||||
|       .all(by.css('app-root div.inputwrapper')) |  | ||||||
|       .get(index) |  | ||||||
|       .element(by.css('.selectwrapper select')); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   getSelectedConverterOption(index: number): promise.Promise<string> { |  | ||||||
|     return this.getConverterDropdown(index) |  | ||||||
|       .$('option:checked') |  | ||||||
|       .getWebElement() |  | ||||||
|       .getText(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   selectConverterOption(index: number, optionName: string): promise.Promise<void> { |  | ||||||
|     return this.getConverterDropdown(index) |  | ||||||
|       .element(by.cssContainingText('option', optionName)) |  | ||||||
|       .click(); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| { |  | ||||||
|   "extends": "../tsconfig.json", |  | ||||||
|   "compilerOptions": { |  | ||||||
|     "outDir": "../out-tsc/app", |  | ||||||
|     "module": "commonjs", |  | ||||||
|     "target": "es5", |  | ||||||
|     "types": [ |  | ||||||
|       "jasmine", |  | ||||||
|       "jasminewd2", |  | ||||||
|       "node" |  | ||||||
|     ] |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										40
									
								
								karma.conf.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								karma.conf.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | // Karma configuration file, see link for more information
 | ||||||
|  | // https://karma-runner.github.io/1.0/config/configuration-file.html
 | ||||||
|  | 
 | ||||||
|  | module.exports = function (config) { | ||||||
|  |   config.set({ | ||||||
|  |     basePath: '', | ||||||
|  |     frameworks: ['jasmine', '@angular-devkit/build-angular'], | ||||||
|  |     plugins: [ | ||||||
|  |       require('karma-jasmine'), | ||||||
|  |       require('karma-chrome-launcher'), | ||||||
|  |       require('karma-firefox-launcher'), | ||||||
|  |       require('karma-jasmine-html-reporter'), | ||||||
|  |       require('karma-coverage'), | ||||||
|  |       require('@angular-devkit/build-angular/plugins/karma') | ||||||
|  |     ], | ||||||
|  |     client: { | ||||||
|  |       jasmine: { | ||||||
|  |         // you can add configuration options for Jasmine here
 | ||||||
|  |         // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
 | ||||||
|  |         // for example, you can disable the random execution with `random: false`
 | ||||||
|  |         // or set a specific seed with `seed: 4321`
 | ||||||
|  |       }, | ||||||
|  |       clearContext: false // leave Jasmine Spec Runner output visible in browser
 | ||||||
|  |     }, | ||||||
|  |     jasmineHtmlReporter: { | ||||||
|  |       suppressAll: true // removes the duplicated traces
 | ||||||
|  |     }, | ||||||
|  |     coverageReporter: { | ||||||
|  |       dir: require('path').join(__dirname, './coverage/convertorizr'), | ||||||
|  |       subdir: '.', | ||||||
|  |       reporters: [ | ||||||
|  |         { type: 'html' }, | ||||||
|  |         { type: 'text-summary' } | ||||||
|  |       ] | ||||||
|  |     }, | ||||||
|  |     reporters: ['progress', 'kjhtml'], | ||||||
|  |     browsers: ['Firefox'], | ||||||
|  |     restartOnFileChange: true | ||||||
|  |   }); | ||||||
|  | }; | ||||||
							
								
								
									
										22147
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										22147
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										75
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										75
									
								
								package.json
									
										
									
									
									
								
							|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
|   "name": "convertorizr", |   "name": "convertorizr", | ||||||
|   "version": "1.3.0", |   "version": "2.0.0-dev.1", | ||||||
|   "description": "Decode or encode base64, punycode, HTML entities, URI components, ...", |   "description": "Decode or encode base64, punycode, HTML entities, URI components, ...", | ||||||
|   "keywords": [ |   "keywords": [ | ||||||
|     "encode", |     "encode", | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
|     "email": "manuel@fritteli.ch" |     "email": "manuel@fritteli.ch" | ||||||
|   }, |   }, | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "homepage": "https://manuel.pages.gittr.ch/dencode.org", |   "homepage": "https://conv.friedli.info/", | ||||||
|   "repository": { |   "repository": { | ||||||
|     "type": "git", |     "type": "git", | ||||||
|     "url": "https://gittr.ch/manuel/converter.git" |     "url": "https://gittr.ch/manuel/converter.git" | ||||||
|  | @ -22,47 +22,44 @@ | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "ng": "ng", |     "ng": "ng", | ||||||
|     "start": "ng serve", |     "start": "ng serve", | ||||||
|     "build": "ng build --delete-output-path", |     "build": "ng build", | ||||||
|     "build-prod": "ng build --prod --optimization --aot --delete-output-path --build-optimizer", |     "watch": "ng build --watch --configuration development", | ||||||
|     "test": "ng test", |     "test": "ng test --browsers=Chromium,Firefox", | ||||||
|     "lint": "ng lint", |     "test:ci": "ng test --no-watch --no-progress --browsers=FirefoxHeadless" | ||||||
|     "e2e": "ng e2e", |  | ||||||
|     "postinstall": "npm rebuild node-sass" |  | ||||||
|   }, |   }, | ||||||
|   "private": true, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@angular/common": "6.1.6", |     "@angular/animations": "^17.1.0", | ||||||
|     "@angular/compiler": "6.1.6", |     "@angular/common": "^17.1.0", | ||||||
|     "@angular/core": "6.1.6", |     "@angular/compiler": "^17.1.0", | ||||||
|     "@angular/forms": "6.1.6", |     "@angular/core": "^17.1.0", | ||||||
|     "@angular/platform-browser": "6.1.6", |     "@angular/forms": "^17.1.0", | ||||||
|     "@angular/platform-browser-dynamic": "6.1.6", |     "@angular/platform-browser": "^17.1.0", | ||||||
|     "@angular/router": "6.1.6", |     "@angular/platform-browser-dynamic": "^17.1.0", | ||||||
|     "core-js": "^2.5.1", |     "@angular/router": "^17.1.0", | ||||||
|     "punycode": "^2.1.0", |     "punycode": "^2.3.1", | ||||||
|     "quoted-printable": "^1.0.0", |     "quoted-printable": "^1.0.1", | ||||||
|     "rxjs": "^6.3.0", |     "rxjs": "~7.8.0", | ||||||
|     "utf8": "^2.1.0", |     "tslib": "^2.3.0", | ||||||
|     "zone.js": "^0.8.26" |     "utf8": "^3.0.0", | ||||||
|  |     "zone.js": "~0.14.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@angular-devkit/build-angular": "~0.7.0", |     "@angular-devkit/build-angular": "^17.1.0", | ||||||
|     "@angular/cli": "6.1.5", |     "@angular/cli": "^17.1.0", | ||||||
|     "@angular/compiler-cli": "6.1.6", |     "@angular/compiler-cli": "^17.1.0", | ||||||
|     "@types/jasmine": "~2.8.6", |     "@types/jasmine": "~5.1.0", | ||||||
|     "@types/jasminewd2": "~2.0.3", |     "@types/node": "^20.11.5", | ||||||
|     "@types/node": "~8.9.4", |     "@types/punycode": "^2.1.3", | ||||||
|     "codelyzer": "~4.2.1", |     "@types/quoted-printable": "^1.0.2", | ||||||
|     "jasmine-core": "~2.99.1", |     "@types/utf8": "^3.0.3", | ||||||
|     "jasmine-spec-reporter": "~4.2.1", |     "jasmine-core": "~5.1.0", | ||||||
|     "karma": "~1.7.1", |     "karma": "~6.4.0", | ||||||
|     "karma-chrome-launcher": "~2.2.0", |     "karma-chrome-launcher": "~3.2.0", | ||||||
|     "karma-coverage-istanbul-reporter": "~2.0.0", |     "karma-coverage": "~2.2.0", | ||||||
|     "karma-jasmine": "~1.1.1", |     "karma-firefox-launcher": "^2.1.2", | ||||||
|     "karma-jasmine-html-reporter": "^0.2.2", |     "karma-jasmine": "~5.1.0", | ||||||
|     "protractor": "~5.4.0", |     "karma-jasmine-html-reporter": "~2.1.0", | ||||||
|     "ts-node": "~5.0.1", |     "typescript": "~5.3.2" | ||||||
|     "tslint": "~5.9.1", |  | ||||||
|     "typescript": "2.9.2" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| import {NgModule} from '@angular/core'; |  | ||||||
| import {RouterModule, Routes} from '@angular/router'; |  | ||||||
| 
 |  | ||||||
| const routes: Routes = [ |  | ||||||
|   { |  | ||||||
|     path: '', |  | ||||||
|     children: [] |  | ||||||
|   } |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| @NgModule({ |  | ||||||
|   imports: [RouterModule.forRoot(routes)], |  | ||||||
|   exports: [RouterModule] |  | ||||||
| }) |  | ||||||
| export class AppRoutingModule { |  | ||||||
| } |  | ||||||
|  | @ -1,17 +1,9 @@ | ||||||
| <div *ngFor="let step of steps" class="inputwrapper"> | @for (step of steps; track step.index) { | ||||||
|   <div class="textwrapper arrow_box"> |   <div class="inputwrapper"> | ||||||
|     <textarea class="textinput" (keyup)="update(step)" placeholder="Please enter your input ..." |     <app-text-input-field [step]="step" #ti></app-text-input-field> | ||||||
|               [(ngModel)]="step.content">{{step.content}}</textarea> |     <app-converter-selector [step]="step" [textInput]="ti"></app-converter-selector> | ||||||
|   </div> |     <app-error-message [step]="step"></app-error-message> | ||||||
|   <div [ngClass]="{selectwrapper: true, error: step.error}"> |  | ||||||
|     <div class="arrow_box"> |  | ||||||
|       <select class="select" (change)="convert(step, $event)"> |  | ||||||
|         <option id="undefined">Select conversion ...</option> |  | ||||||
|         <option class="option" *ngFor="let c of converters" id="{{c.getId()}}">{{c.getDisplayname()}} |  | ||||||
|         </option> |  | ||||||
|       </select> |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
|   <div class="errormessage" *ngIf="step.error" [innerHTML]="step.message"></div> |  | ||||||
|   </div> |   </div> | ||||||
|  | } | ||||||
|  | <app-version></app-version> | ||||||
| <!--<router-outlet></router-outlet>--> | <!--<router-outlet></router-outlet>--> | ||||||
|  |  | ||||||
|  | @ -2,106 +2,3 @@ | ||||||
|   font-family: "ABeeZee", sans-serif; |   font-family: "ABeeZee", sans-serif; | ||||||
|   margin: 0 1em 1em 1em; |   margin: 0 1em 1em 1em; | ||||||
| } | } | ||||||
| 
 |  | ||||||
| .textwrapper { |  | ||||||
|   margin: 0 0 1em 0; |  | ||||||
|   padding: 0 1em 0 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .arrow_box { |  | ||||||
|   position: relative; |  | ||||||
|   background: #fff; |  | ||||||
|   border: 1px solid #aaa; |  | ||||||
|   &:focus { |  | ||||||
|     border-color: #888; |  | ||||||
|   } |  | ||||||
|   &:hover { |  | ||||||
|     border-color: #333; |  | ||||||
|   } |  | ||||||
|   &:after, &:before { |  | ||||||
|     top: 100%; |  | ||||||
|     left: 50%; |  | ||||||
|     border: solid transparent; |  | ||||||
|     content: " "; |  | ||||||
|     height: 0; |  | ||||||
|     width: 0; |  | ||||||
|     position: absolute; |  | ||||||
|     pointer-events: none; |  | ||||||
|   } |  | ||||||
|   &:after { |  | ||||||
|     border-color: rgba(255, 255, 255, 0); |  | ||||||
|     border-top-color: #fff; |  | ||||||
|     border-width: 1em; |  | ||||||
|     margin-left: -1em; |  | ||||||
|   } |  | ||||||
|   &:before { |  | ||||||
|     border-color: rgba(170, 170, 170, 0); |  | ||||||
|     border-top-color: #aaa; |  | ||||||
|     border-width: calc(1em + 1px); |  | ||||||
|     margin-left: calc(-1em - 1px); |  | ||||||
|   } |  | ||||||
|   &:focus:before { |  | ||||||
|     border-color: rgba(136, 136, 136, 0); |  | ||||||
|     border-top-color: #888; |  | ||||||
|   } |  | ||||||
|   &:hover:before { |  | ||||||
|     border-color: rgba(51, 51, 51, 0); |  | ||||||
|     border-top-color: #333; |  | ||||||
|   } |  | ||||||
|   .selectwrapper > & { |  | ||||||
|     display: inline-block; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .textinput { |  | ||||||
|   background-color: #fff; |  | ||||||
|   border: none; |  | ||||||
|   color: #000; |  | ||||||
|   font-family: "Free Monospaced", monospace; |  | ||||||
|   height: 10em; |  | ||||||
|   margin: 0; |  | ||||||
|   padding: 0.5em; |  | ||||||
|   resize: vertical; |  | ||||||
|   width: 100%; |  | ||||||
|   &:focus { |  | ||||||
|     border-color: #888; |  | ||||||
|   } |  | ||||||
|   &:hover { |  | ||||||
|     border-color: #333; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .selectwrapper { |  | ||||||
|   margin: 0 0 1em 0; |  | ||||||
|   padding: 0; |  | ||||||
|   text-align: center; |  | ||||||
|   &.error { |  | ||||||
|     > .arrow_box { |  | ||||||
|       border-color: red; |  | ||||||
|       &:before { |  | ||||||
|         border-top-color: red; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     select { |  | ||||||
|       color: red; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .select { |  | ||||||
|   background-color: #fff; |  | ||||||
|   border: none; |  | ||||||
|   color: #000; |  | ||||||
|   font-family: "ABeeZee", sans-serif; |  | ||||||
|   margin: 0; |  | ||||||
|   padding: 0.5em; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .option { |  | ||||||
|   /*    font-family: "ABeeZee", sans-serif;*/ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .errormessage { |  | ||||||
|   color: red; |  | ||||||
|   text-align: center; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  | @ -1,53 +1,26 @@ | ||||||
| import {AppComponent} from './app.component'; | import {AppComponent} from './app.component'; | ||||||
| import {async, ComponentFixture, TestBed} from '@angular/core/testing'; | import {ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
| import {FormsModule} from '@angular/forms'; |  | ||||||
| import {InputComponentManagerService} from './inputcomponentmanager.service'; |  | ||||||
| import {Step} from './step'; | import {Step} from './step'; | ||||||
| import {ConverterRegistryService} from './converterregistry.service'; |  | ||||||
| import {Converter} from './converter/converter'; |  | ||||||
| 
 | 
 | ||||||
| describe('AppComponent', () => { | describe('AppComponent', () => { | ||||||
|   let sut: AppComponent; |   let sut: AppComponent; | ||||||
|   let fixture: ComponentFixture<AppComponent>; |   let fixture: ComponentFixture<AppComponent>; | ||||||
|   const firstStep: Step = new Step(0); |   const firstStep: Step = new Step(0); | ||||||
| 
 | 
 | ||||||
|   const inputComponentManagerServiceStub = { |   beforeEach(async () => { | ||||||
|     getFirst: () => { |     await TestBed.configureTestingModule({ | ||||||
|       return firstStep; |       imports: [AppComponent] | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const converterRegistryServiceStub = { |  | ||||||
|     getAllConverters: (): Converter[] => { |  | ||||||
|       return []; |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     getConverter: (id: string): Converter => { |  | ||||||
|       return undefined; |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   beforeEach(async(() => { |  | ||||||
|     /*return */ |  | ||||||
|     TestBed.configureTestingModule({ |  | ||||||
|       declarations: [AppComponent], |  | ||||||
|       imports: [FormsModule], |  | ||||||
|       providers: [ |  | ||||||
|         {provide: InputComponentManagerService, useValue: inputComponentManagerServiceStub}, |  | ||||||
|         {provide: ConverterRegistryService, useValue: converterRegistryServiceStub} |  | ||||||
|       ] |  | ||||||
|     }) |     }) | ||||||
|       .compileComponents(); |       .compileComponents(); | ||||||
|   })); |   }); | ||||||
| 
 | 
 | ||||||
|   beforeEach(() => { |   beforeEach(() => { | ||||||
|     fixture = TestBed.createComponent(AppComponent); |     fixture = TestBed.createComponent(AppComponent); | ||||||
|     sut = fixture.componentInstance; |     sut = fixture.componentInstance; | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('should create the app', async(() => { |   it('should create the app', () => { | ||||||
|     // const fixture = TestBed.createComponent(AppComponent);
 |     // const fixture = TestBed.createComponent(AppComponent);
 | ||||||
|     const app = fixture.debugElement.componentInstance; |     expect(sut).toBeTruthy(); | ||||||
|     expect(app).toBeTruthy(); |   }); | ||||||
|   })); |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,57 +1,32 @@ | ||||||
| import {Component, OnInit} from '@angular/core'; | import {Component, OnInit} from '@angular/core'; | ||||||
| import {ConverterRegistryService} from './converterregistry.service'; | import {InputComponentManagerService} from './input-component-manager.service'; | ||||||
| import {InputComponentManagerService} from './inputcomponentmanager.service'; |  | ||||||
| import {Step} from './step'; | import {Step} from './step'; | ||||||
| import {Converter} from './converter/converter'; | import {RouterOutlet} from '@angular/router'; | ||||||
|  | import {TextInputFieldComponent} from './text-input-field/text-input-field.component'; | ||||||
|  | import {ConverterSelectorComponent} from './converter-selector/converter-selector.component'; | ||||||
|  | import {ErrorMessageComponent} from './error-message/error-message.component'; | ||||||
|  | import {VersionComponent} from './version/version.component'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'app-root', |   selector: 'app-root', | ||||||
|  |   standalone: true, | ||||||
|  |   imports: [ | ||||||
|  |     ConverterSelectorComponent, | ||||||
|  |     ErrorMessageComponent, | ||||||
|  |     RouterOutlet, | ||||||
|  |     TextInputFieldComponent, | ||||||
|  |     VersionComponent | ||||||
|  |   ], | ||||||
|   templateUrl: './app.component.html', |   templateUrl: './app.component.html', | ||||||
|   styleUrls: ['./app.component.scss'] |   styleUrl: './app.component.scss' | ||||||
| }) | }) | ||||||
| export class AppComponent implements OnInit { | export class AppComponent implements OnInit { | ||||||
|   public steps: Step[] = []; |   public steps: Step[] = []; | ||||||
|   public converters: Converter[] = []; |  | ||||||
| 
 | 
 | ||||||
|   constructor(private converterRegistryService: ConverterRegistryService, |   constructor(private inputComponentManagerService: InputComponentManagerService) { | ||||||
|               private inputComponentManagerService: InputComponentManagerService) { |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   convert(step: Step, $event: any): void { |  | ||||||
|     step.selectedConverter = this.converterRegistryService.getConverter($event.target.selectedOptions[0].id); |  | ||||||
|     this.update(step); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   update(step: Step): void { |  | ||||||
|     const converter: Converter = step.selectedConverter; |  | ||||||
| 
 |  | ||||||
|     if (converter !== undefined) { |  | ||||||
|       const content: string = step.content; |  | ||||||
|       let result: string; |  | ||||||
|       try { |  | ||||||
|         result = converter.convert(content); |  | ||||||
|       } catch (error) { |  | ||||||
|         if (typeof console === 'object' && typeof console.log === 'function') { |  | ||||||
|           console.log(error); |  | ||||||
|         } |  | ||||||
|         step.message = error.message; |  | ||||||
|         step.error = true; |  | ||||||
|         result = null; |  | ||||||
|       } |  | ||||||
|       if (result !== null) { |  | ||||||
|         step.message = ''; |  | ||||||
|         step.error = false; |  | ||||||
|         if (result !== '') { |  | ||||||
|           const nextComponent: Step = this.inputComponentManagerService.getNext(step); |  | ||||||
|           nextComponent.content = result; |  | ||||||
|           this.update(nextComponent); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ngOnInit(): void { |   ngOnInit(): void { | ||||||
|     this.converters = this.converterRegistryService.getAllConverters(); |  | ||||||
|     this.steps = this.inputComponentManagerService.getAllComponents(); |     this.steps = this.inputComponentManagerService.getAllComponents(); | ||||||
|     this.inputComponentManagerService.getFirst(); |     this.inputComponentManagerService.getFirst(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
							
								
								
									
										8
									
								
								src/app/app.config.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/app/app.config.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | import { ApplicationConfig } from '@angular/core'; | ||||||
|  | import { provideRouter } from '@angular/router'; | ||||||
|  | 
 | ||||||
|  | import { routes } from './app.routes'; | ||||||
|  | 
 | ||||||
|  | export const appConfig: ApplicationConfig = { | ||||||
|  |   providers: [provideRouter(routes)] | ||||||
|  | }; | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| import {BrowserModule} from '@angular/platform-browser'; |  | ||||||
| import {NgModule} from '@angular/core'; |  | ||||||
| import {AppComponent} from './app.component'; |  | ||||||
| import {ConverterRegistryService} from './converterregistry.service'; |  | ||||||
| import {InputComponentManagerService} from './inputcomponentmanager.service'; |  | ||||||
| import {NativeLibraryWrapperService} from './nativelibrarywrapper.service'; |  | ||||||
| import {FormsModule} from '@angular/forms'; |  | ||||||
| 
 |  | ||||||
| @NgModule({ |  | ||||||
|   declarations: [ |  | ||||||
|     AppComponent |  | ||||||
|   ], |  | ||||||
|   imports: [ |  | ||||||
|     BrowserModule, |  | ||||||
|     FormsModule |  | ||||||
|   ], |  | ||||||
|   providers: [ConverterRegistryService, InputComponentManagerService, NativeLibraryWrapperService], |  | ||||||
|   bootstrap: [AppComponent] |  | ||||||
| }) |  | ||||||
| export class AppModule { |  | ||||||
| } |  | ||||||
							
								
								
									
										3
									
								
								src/app/app.routes.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/app.routes.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | import { Routes } from '@angular/router'; | ||||||
|  | 
 | ||||||
|  | export const routes: Routes = []; | ||||||
							
								
								
									
										40
									
								
								src/app/converter-registry.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/app/converter-registry.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | import {inject, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {ConverterRegistryService} from './converter-registry.service'; | ||||||
|  | import {NativeLibraryWrapperService} from './native-library-wrapper.service'; | ||||||
|  | import {Converter} from './converter/converter'; | ||||||
|  | import {Base64Decoder} from './converter/base64-decoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | 
 | ||||||
|  | describe('ConverterRegistryService', () => { | ||||||
|  |   beforeEach(() => { | ||||||
|  |     TestBed.configureTestingModule({ | ||||||
|  |       providers: [ | ||||||
|  |         ConverterRegistryService, | ||||||
|  |         {provide: NativeLibraryWrapperService, useValue: createSpyObj(['punycode', 'quotedPrintable', 'utf8'])} | ||||||
|  |       ] | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should be created', inject([ConverterRegistryService], (service: ConverterRegistryService) => { | ||||||
|  |     expect(service).toBeTruthy(); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should register converters upon creation', inject([ConverterRegistryService], (service: ConverterRegistryService) => { | ||||||
|  |     expect(service.getAllConverters()).toBeTruthy(); | ||||||
|  |     expect(service.getAllConverters().length).toBeGreaterThan(0); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('must not allow the same converter ID to be regisgered more than once', | ||||||
|  |     inject([ConverterRegistryService], (service: ConverterRegistryService) => { | ||||||
|  |       // arrange
 | ||||||
|  |       const duplicateConverter: Converter = new Base64Decoder(); | ||||||
|  |       const duplicateConverterId = duplicateConverter.getId(); | ||||||
|  |       expect(() => { | ||||||
|  |         // act
 | ||||||
|  |         (service as any).registerConverter(duplicateConverter); | ||||||
|  |       }) | ||||||
|  |       // assert
 | ||||||
|  |         .toThrowError(`Converter-ID ${duplicateConverterId} is already registered!`); | ||||||
|  |     })); | ||||||
|  | }); | ||||||
|  | @ -1,27 +1,29 @@ | ||||||
| import {Injectable} from '@angular/core'; | import {Base64Decoder} from './converter/base64-decoder'; | ||||||
|  | import {Base64Encoder} from './converter/base64-encoder'; | ||||||
|  | import {BinToDecConverter} from './converter/bin-to-dec-converter'; | ||||||
| import {Converter} from './converter/converter'; | import {Converter} from './converter/converter'; | ||||||
| import {Base64Encoder} from './converter/base64encoder'; | import {DecToBinConverter} from './converter/dec-to-bin-converter'; | ||||||
| import {Base64Decoder} from './converter/base64decoder'; | import {DecToHexConverter} from './converter/dec-to-hex-converter'; | ||||||
| import {URIEncoder} from './converter/uriencoder'; | import {HexToDecConverter} from './converter/hex-to-dec-converter'; | ||||||
|  | import {HTMLEntitiesDecoder} from './converter/htmlentities-decoder'; | ||||||
|  | import {HTMLEntitiesEncoder} from './converter/htmlentities-encoder'; | ||||||
|  | import {Injectable} from '@angular/core'; | ||||||
|  | import {NativeLibraryWrapperService} from './native-library-wrapper.service'; | ||||||
|  | import {PunycodeDecoder} from './converter/punycode-decoder'; | ||||||
|  | import {PunycodeEncoder} from './converter/punycode-encoder'; | ||||||
|  | import {QuotedPrintableDecoder} from './converter/quoted-printable-decoder'; | ||||||
|  | import {QuotedPrintableEncoder} from './converter/quoted-printable-encoder'; | ||||||
|  | import {ROT13Converter} from './converter/rot13-converter'; | ||||||
|  | import {URIComponentDecoder} from './converter/uricomponent-decoder'; | ||||||
|  | import {URIComponentEncoder} from './converter/uricomponent-encoder'; | ||||||
| import {URIDecoder} from './converter/uridecoder'; | import {URIDecoder} from './converter/uridecoder'; | ||||||
| import {URIComponentEncoder} from './converter/uricomponentencoder'; | import {URIEncoder} from './converter/uriencoder'; | ||||||
| import {URIComponentDecoder} from './converter/uricomponentdecoder'; | import {UTF8Decoder} from './converter/utf8-decoder'; | ||||||
| import {HTMLEntitiesEncoder} from './converter/htmlentitiesencoder'; | import {UTF8Encoder} from './converter/utf8-encoder'; | ||||||
| import {HTMLEntitiesDecoder} from './converter/htmlentitiesdecoder'; |  | ||||||
| import {DecToHexConverter} from './converter/dectohexconverter'; |  | ||||||
| import {HexToDecConverter} from './converter/hextodecconverter'; |  | ||||||
| import {DecToBinConverter} from './converter/dectobinconverter'; |  | ||||||
| import {BinToDecConverter} from './converter/bintodecconverter'; |  | ||||||
| import {QuotedPrintableDecoder} from './converter/quotedprintabledecoder'; |  | ||||||
| import {QuotedPrintableEncoder} from './converter/quotedprintableencoder'; |  | ||||||
| import {NativeLibraryWrapperService} from './nativelibrarywrapper.service'; |  | ||||||
| import {PunycodeEncoder} from './converter/punycodeencoder'; |  | ||||||
| import {PunycodeDecoder} from './converter/punycodedecoder'; |  | ||||||
| import {UTF8Encoder} from './converter/utf8encoder'; |  | ||||||
| import {UTF8Decoder} from './converter/utf8decoder'; |  | ||||||
| import {ROT13Converter} from './converter/rot13converter'; |  | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable({ | ||||||
|  |   providedIn: 'root' | ||||||
|  | }) | ||||||
| export class ConverterRegistryService { | export class ConverterRegistryService { | ||||||
|   private converters: Converter[] = []; |   private converters: Converter[] = []; | ||||||
| 
 | 
 | ||||||
|  | @ -33,13 +35,8 @@ export class ConverterRegistryService { | ||||||
|     return this.converters; |     return this.converters; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public getConverter(id: string): Converter { |   public getConverter(id: string): Converter | undefined { | ||||||
|     for (let i = 0; i < this.converters.length; i++) { |     return this.converters.find((converter: Converter): boolean => converter.getId() === id); | ||||||
|       if (this.converters[i].getId() === id) { |  | ||||||
|         return this.converters[i]; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return undefined; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private init(): void { |   private init(): void { | ||||||
|  | @ -65,11 +62,11 @@ export class ConverterRegistryService { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private registerConverter(converter: Converter): void { |   private registerConverter(converter: Converter): void { | ||||||
|     this.converters.forEach((c: Converter) => { |     // Don't allow duplicate registration of the same converter id
 | ||||||
|       if (c.getId() === converter.getId()) { |     if (this.converters.some((c: Converter): boolean => c.getId() === converter.getId())) { | ||||||
|         throw new Error('Converter-ID ' + converter.getId() + ' is already registered!'); |       throw new Error(`Converter-ID ${converter.getId()} is already registered!`); | ||||||
|     } |     } | ||||||
|     }); | 
 | ||||||
|     this.converters.push(converter); |     this.converters.push(converter); | ||||||
|   } |   } | ||||||
| } | } | ||||||
							
								
								
									
										10
									
								
								src/app/converter-selector/converter-selector.component.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/app/converter-selector/converter-selector.component.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | <div [ngClass]="{selectwrapper: true, error: step.error}"> | ||||||
|  |   <div class="arrow_box"> | ||||||
|  |     <select class="select" (change)="convert($event)"> | ||||||
|  |       <option id="undefined">Select conversion ...</option> | ||||||
|  |       @for (c of converters; track c.getId()) { | ||||||
|  |         <option class="option" id="{{c.getId()}}">{{ c.getDisplayname() }}</option> | ||||||
|  |       } | ||||||
|  |     </select> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
							
								
								
									
										74
									
								
								src/app/converter-selector/converter-selector.component.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/app/converter-selector/converter-selector.component.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | .selectwrapper { | ||||||
|  |   margin: 0 0 1em 0; | ||||||
|  |   padding: 0; | ||||||
|  |   text-align: center; | ||||||
|  |   &.error { | ||||||
|  |     > .arrow_box { | ||||||
|  |       border-color: red; | ||||||
|  |       &:before { | ||||||
|  |         border-top-color: red; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     select { | ||||||
|  |       color: red; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .select { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border: none; | ||||||
|  |   color: #000; | ||||||
|  |   font-family: "ABeeZee", sans-serif; | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 0.5em; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .arrow_box { | ||||||
|  |   position: relative; | ||||||
|  |   background: #fff; | ||||||
|  |   border: 1px solid #aaa; | ||||||
|  |   &:focus { | ||||||
|  |     border-color: #888; | ||||||
|  |   } | ||||||
|  |   &:hover { | ||||||
|  |     border-color: #333; | ||||||
|  |   } | ||||||
|  |   &:after, &:before { | ||||||
|  |     top: 100%; | ||||||
|  |     left: 50%; | ||||||
|  |     border: solid transparent; | ||||||
|  |     content: " "; | ||||||
|  |     height: 0; | ||||||
|  |     width: 0; | ||||||
|  |     position: absolute; | ||||||
|  |     pointer-events: none; | ||||||
|  |   } | ||||||
|  |   &:after { | ||||||
|  |     border-color: rgba(255, 255, 255, 0); | ||||||
|  |     border-top-color: #fff; | ||||||
|  |     border-width: 1em; | ||||||
|  |     margin-left: -1em; | ||||||
|  |   } | ||||||
|  |   &:before { | ||||||
|  |     border-color: rgba(170, 170, 170, 0); | ||||||
|  |     border-top-color: #aaa; | ||||||
|  |     border-width: calc(1em + 1px); | ||||||
|  |     margin-left: calc(-1em - 1px); | ||||||
|  |   } | ||||||
|  |   &:focus:before { | ||||||
|  |     border-color: rgba(136, 136, 136, 0); | ||||||
|  |     border-top-color: #888; | ||||||
|  |   } | ||||||
|  |   &:hover:before { | ||||||
|  |     border-color: rgba(51, 51, 51, 0); | ||||||
|  |     border-top-color: #333; | ||||||
|  |   } | ||||||
|  |   .selectwrapper > & { | ||||||
|  |     display: inline-block; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .option { | ||||||
|  |   /*    font-family: "ABeeZee", sans-serif;*/ | ||||||
|  | } | ||||||
|  | @ -0,0 +1,93 @@ | ||||||
|  | import {ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {ConverterSelectorComponent} from './converter-selector.component'; | ||||||
|  | import {Component, ViewChild} from '@angular/core'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | import {TextInputFieldComponent} from '../text-input-field/text-input-field.component'; | ||||||
|  | import {InputComponentManagerService} from '../input-component-manager.service'; | ||||||
|  | import {ConverterRegistryService} from '../converter-registry.service'; | ||||||
|  | import {Converter} from '../converter/converter'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import SpyObj = jasmine.SpyObj; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   template: '<app-converter-selector [step]="step" [textInput]="textInputComponent"></app-converter-selector>' | ||||||
|  | }) | ||||||
|  | class TestHostComponent { | ||||||
|  |   public step: Step = new Step(42); | ||||||
|  |   public textInputComponent: TextInputFieldComponent = new TextInputFieldComponent(inputComponentManagerServiceStub); | ||||||
|  |   @ViewChild(ConverterSelectorComponent) | ||||||
|  |   public sutComponent!: ConverterSelectorComponent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const inputComponentManagerServiceStub = createSpyObj(['getNext']); | ||||||
|  | 
 | ||||||
|  | describe('ConverterSelectorComponent', () => { | ||||||
|  |   let testHostComponent: TestHostComponent; | ||||||
|  |   let testHostFixture: ComponentFixture<TestHostComponent>; | ||||||
|  | 
 | ||||||
|  |   const converter1: SpyObj<Converter> = createSpyObj(['getId', 'getDisplayname', 'convert']); | ||||||
|  |   const converter2: SpyObj<Converter> = createSpyObj(['getId', 'getDisplayname', 'convert']); | ||||||
|  |   const converter3: SpyObj<Converter> = createSpyObj(['getId', 'getDisplayname', 'convert']); | ||||||
|  |   // converter1.getId.and.returnValue('converter1');
 | ||||||
|  |   converter1.getDisplayname.and.returnValue('Converter 1'); | ||||||
|  |   // converter2.getId.and.returnValue('converter2');
 | ||||||
|  |   converter2.getDisplayname.and.returnValue('Converter 2'); | ||||||
|  |   // converter3.getId.and.returnValue('converter3');
 | ||||||
|  |   converter3.getDisplayname.and.returnValue('Converter 3'); | ||||||
|  |   const converterRegistryServiceStub = createSpyObj(['getAllConverters', 'getConverter']); | ||||||
|  |   converterRegistryServiceStub.getAllConverters.and.returnValue([converter1, converter2, converter3]); | ||||||
|  |   converterRegistryServiceStub.getConverter.and.returnValue(undefined); | ||||||
|  | 
 | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await TestBed.configureTestingModule({ | ||||||
|  |       imports: [ConverterSelectorComponent], | ||||||
|  |       declarations: [ | ||||||
|  |         TestHostComponent | ||||||
|  |       ], | ||||||
|  |       providers: [ | ||||||
|  |         {provide: InputComponentManagerService, useValue: inputComponentManagerServiceStub}, | ||||||
|  |         {provide: ConverterRegistryService, useValue: converterRegistryServiceStub} | ||||||
|  |       ] | ||||||
|  |     }) | ||||||
|  |       .compileComponents(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     testHostFixture = TestBed.createComponent(TestHostComponent); | ||||||
|  |     testHostComponent = testHostFixture.componentInstance; | ||||||
|  |     testHostFixture.detectChanges(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create the component', () => { | ||||||
|  |     expect(testHostComponent.sutComponent).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should update the conversion when the selection changes', () => { | ||||||
|  |     // arrange
 | ||||||
|  |     // set next step
 | ||||||
|  |     const nextStep: Step = new Step(99); | ||||||
|  |     inputComponentManagerServiceStub.getNext.and.returnValue(nextStep); | ||||||
|  |     // set up converter
 | ||||||
|  |     const dummyConverter = createSpyObj(['convert']); | ||||||
|  |     dummyConverter.convert.and.returnValue('Converted value'); | ||||||
|  |     converterRegistryServiceStub.getConverter.and.returnValue(dummyConverter); | ||||||
|  |     // create event structure
 | ||||||
|  |     const event = {target: {selectedOptions: [{id: 'The testing converter id'}]}}; | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     testHostComponent.sutComponent.convert(event); | ||||||
|  | 
 | ||||||
|  |     // assert
 | ||||||
|  |     expect(converterRegistryServiceStub.getConverter).toHaveBeenCalledWith('The testing converter id'); | ||||||
|  |     expect(nextStep.content).toEqual('Converted value'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should load available converters upon creation', () => { | ||||||
|  |     const converters: Converter[] = testHostComponent.sutComponent.converters; | ||||||
|  |     expect(converters.length).toEqual(3); | ||||||
|  |     expect(converters[0]).toEqual(converter1); | ||||||
|  |     expect(converters[1]).toEqual(converter2); | ||||||
|  |     expect(converters[2]).toEqual(converter3); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										33
									
								
								src/app/converter-selector/converter-selector.component.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/app/converter-selector/converter-selector.component.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | import {Component, Input, OnInit} from '@angular/core'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | import {Converter} from '../converter/converter'; | ||||||
|  | import {ConverterRegistryService} from '../converter-registry.service'; | ||||||
|  | import {TextInputFieldComponent} from '../text-input-field/text-input-field.component'; | ||||||
|  | import {CommonModule} from '@angular/common'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-converter-selector', | ||||||
|  |   templateUrl: './converter-selector.component.html', | ||||||
|  |   standalone: true, | ||||||
|  |   styleUrls: ['./converter-selector.component.scss'], | ||||||
|  |   imports: [CommonModule] | ||||||
|  | }) | ||||||
|  | export class ConverterSelectorComponent implements OnInit { | ||||||
|  |   @Input() | ||||||
|  |   public step!: Step; | ||||||
|  |   @Input() | ||||||
|  |   textInput!: TextInputFieldComponent; | ||||||
|  |   public converters: Converter[] = []; | ||||||
|  | 
 | ||||||
|  |   constructor(private converterRegistryService: ConverterRegistryService) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   convert($event: any): void { | ||||||
|  |     this.step.selectedConverter = this.converterRegistryService.getConverter($event.target.selectedOptions[0].id); | ||||||
|  |     this.textInput.update(this.step); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ngOnInit() { | ||||||
|  |     this.converters = this.converterRegistryService.getAllConverters(); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								src/app/converter/base64-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/base64-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {Base64Decoder} from './base64-decoder'; | ||||||
|  | 
 | ||||||
|  | describe('Base64Decoder', () => { | ||||||
|  |   let sut: Base64Decoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new Base64Decoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "base64decode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('base64decode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should decode "SGVsbG8sIFdvcmxkIQ==" to "Hello, World!"', () => { | ||||||
|  |     expect(sut.convert('SGVsbG8sIFdvcmxkIQ==')).toEqual('Hello, World!'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('foo bar.')).toThrowError('Could not decode base64 string. Maybe corrupt input?'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										27
									
								
								src/app/converter/base64-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/base64-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {Base64Encoder} from './base64-encoder'; | ||||||
|  | 
 | ||||||
|  | describe('Base64Encoder', () => { | ||||||
|  |   let sut: Base64Encoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new Base64Encoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "base64encode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('base64encode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should encode "Oh, guete Tag!" to "T2gsIGd1ZXRlIFRhZyE="', () => { | ||||||
|  |     expect(sut.convert('Oh, guete Tag!')).toEqual('T2gsIGd1ZXRlIFRhZyE='); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('€')).toThrowError(/Looks like you've got a character outside of the Latin1 range there./); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -14,7 +14,7 @@ export class Base64Encoder implements Converter { | ||||||
|       return btoa(input); |       return btoa(input); | ||||||
|     } catch (exception) { |     } catch (exception) { | ||||||
|       console.error(exception); |       console.error(exception); | ||||||
|       throw new Error('Ouch! Looks like you\'ve got a UTF-8 character there. Too bad, this is not supported yet. ' |       throw new Error('Ouch! Looks like you\'ve got a character outside of the Latin1 range there. Too bad, this is not supported yet. ' | ||||||
|         + 'We\'re working on it and hope to be ready soon! Why don\'t you ' |         + 'We\'re working on it and hope to be ready soon! Why don\'t you ' | ||||||
|         + '<a href="https://duckduckgo.com/?q=cute+kitties&iar=images">enjoy some kittens</a> meanwhile?'); |         + '<a href="https://duckduckgo.com/?q=cute+kitties&iar=images">enjoy some kittens</a> meanwhile?'); | ||||||
|     } |     } | ||||||
							
								
								
									
										27
									
								
								src/app/converter/bin-to-dec-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/bin-to-dec-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {BinToDecConverter} from './bin-to-dec-converter'; | ||||||
|  | 
 | ||||||
|  | describe('BinToDecConverter', () => { | ||||||
|  |   let sut: BinToDecConverter; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new BinToDecConverter()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "bintodec"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('bintodec'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should convert "11011" to "27"', () => { | ||||||
|  |     expect(sut.convert('11011')).toEqual('27'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('1foo bar')).toThrowError('The input seems not to be a valid binary number.'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -11,7 +11,7 @@ export class BinToDecConverter implements Converter { | ||||||
| 
 | 
 | ||||||
|   convert(input: string): string { |   convert(input: string): string { | ||||||
|     const n: number = parseInt(input, 2); |     const n: number = parseInt(input, 2); | ||||||
|     if (isNaN(n)) { |     if (isNaN(n) || !input.trim().match(/^([01]+)$/)) { | ||||||
|       throw new Error('The input seems not to be a valid binary number.'); |       throw new Error('The input seems not to be a valid binary number.'); | ||||||
|     } |     } | ||||||
|     return n.toString(10); |     return n.toString(10); | ||||||
							
								
								
									
										27
									
								
								src/app/converter/dec-to-bin-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/dec-to-bin-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {DecToBinConverter} from './dec-to-bin-converter'; | ||||||
|  | 
 | ||||||
|  | describe('DecToBinConverter', () => { | ||||||
|  |   let sut: DecToBinConverter; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new DecToBinConverter()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "dectobin"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('dectobin'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should convert "22" to "10110"', () => { | ||||||
|  |     expect(sut.convert('22')).toEqual('10110'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('foo bar')).toThrowError('The input seems not to be a valid integer.'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										27
									
								
								src/app/converter/dec-to-hex-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/dec-to-hex-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {DecToHexConverter} from './dec-to-hex-converter'; | ||||||
|  | 
 | ||||||
|  | describe('DecToHexConverter', () => { | ||||||
|  |   let sut: DecToHexConverter; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new DecToHexConverter()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "dectohex"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('dectohex'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should convert "22" to "16"', () => { | ||||||
|  |     expect(sut.convert('22')).toEqual('16'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('foo bar')).toThrowError('The input seems not to be a valid integer.'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										27
									
								
								src/app/converter/hex-to-dec-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/app/converter/hex-to-dec-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | import {HexToDecConverter} from './hex-to-dec-converter'; | ||||||
|  | 
 | ||||||
|  | describe('HexToDecConverter', () => { | ||||||
|  |   let sut: HexToDecConverter; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new HexToDecConverter()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "hextodec"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('hextodec'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should convert "ab" to "171"', () => { | ||||||
|  |     expect(sut.convert('ab')).toEqual('171'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should raise an exception on invalid input', () => { | ||||||
|  |     expect(() => sut.convert('foo bar')).toThrowError('The input seems not to be a valid hexadecimal number.'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -11,7 +11,7 @@ export class HexToDecConverter implements Converter { | ||||||
| 
 | 
 | ||||||
|   convert(input: string): string { |   convert(input: string): string { | ||||||
|     const n: number = parseInt(input, 16); |     const n: number = parseInt(input, 16); | ||||||
|     if (isNaN(n)) { |     if (isNaN(n) || !input.trim().match(/^((0x|0X)?[0-9a-fA-F]+)$/)) { | ||||||
|       throw new Error('The input seems not to be a valid hexadecimal number.'); |       throw new Error('The input seems not to be a valid hexadecimal number.'); | ||||||
|     } |     } | ||||||
|     return n.toString(10); |     return n.toString(10); | ||||||
							
								
								
									
										24
									
								
								src/app/converter/htmlentities-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/app/converter/htmlentities-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | import {HTMLEntitiesDecoder} from './htmlentities-decoder'; | ||||||
|  | 
 | ||||||
|  | describe('HTMLEntitiesDecoder', () => { | ||||||
|  |   let sut: HTMLEntitiesDecoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new HTMLEntitiesDecoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "decodehtmlentities"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('decodehtmlentities'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should decode "<span>"Hi" & "Lo"</span>" to "<span>"Hi" & "Lo"</span>"', () => { | ||||||
|  |     expect(sut.convert('<span>"Hi" & "Lo"</span>')) | ||||||
|  |       .toEqual('<span>"Hi" & "Lo"</span>'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										24
									
								
								src/app/converter/htmlentities-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/app/converter/htmlentities-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | ||||||
|  | import {HTMLEntitiesEncoder} from './htmlentities-encoder'; | ||||||
|  | 
 | ||||||
|  | describe('HTMLEntitiesEncoder', () => { | ||||||
|  |   let sut: HTMLEntitiesEncoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new HTMLEntitiesEncoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "encodehtmlentities"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('encodehtmlentities'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should encode "<span>"Hi" & "Lo"</span>" to "<span>"Hi" & "Lo"</span>"', () => { | ||||||
|  |     expect(sut.convert('<span>"Hi" & "Lo"</span>')) | ||||||
|  |       .toEqual('<span>"Hi" & "Lo"</span>'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										37
									
								
								src/app/converter/punycode-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/punycode-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {PunycodeDecoder} from './punycode-decoder'; | ||||||
|  | import {NativeLibraryWrapperService, Punycode} from '../native-library-wrapper.service'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('PunycodeDecoder', () => { | ||||||
|  |   let sut: PunycodeDecoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let decodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {punycode: createSpyObj<Punycode>(['decode'])}; | ||||||
|  |     decodeSpy = nativeWrapperSpy.punycode!.decode as Spy; | ||||||
|  |     sut = new PunycodeDecoder((nativeWrapperSpy as NativeLibraryWrapperService)); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "decodepunycode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('decodepunycode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native punycode decoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     decodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(decodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class PunycodeDecoder implements Converter { | export class PunycodeDecoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										37
									
								
								src/app/converter/punycode-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/punycode-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {NativeLibraryWrapperService, Punycode} from '../native-library-wrapper.service'; | ||||||
|  | import {PunycodeEncoder} from './punycode-encoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('PunycodeEncoder', () => { | ||||||
|  |   let sut: PunycodeEncoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let encodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {punycode: createSpyObj<Punycode>(['encode'])}; | ||||||
|  |     encodeSpy = nativeWrapperSpy.punycode!.encode as Spy; | ||||||
|  |     sut = new PunycodeEncoder(nativeWrapperSpy as NativeLibraryWrapperService); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "encodepunycode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('encodepunycode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native punycode encoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     encodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(encodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class PunycodeEncoder implements Converter { | export class PunycodeEncoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										37
									
								
								src/app/converter/quoted-printable-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/quoted-printable-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {NativeLibraryWrapperService, QuotedPrintable} from '../native-library-wrapper.service'; | ||||||
|  | import {QuotedPrintableDecoder} from './quoted-printable-decoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('QuotedPrintableDecoder', () => { | ||||||
|  |   let sut: QuotedPrintableDecoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let decodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {quotedPrintable: createSpyObj<QuotedPrintable>(['decode'])}; | ||||||
|  |     decodeSpy = nativeWrapperSpy.quotedPrintable!.decode as Spy; | ||||||
|  |     sut = new QuotedPrintableDecoder((nativeWrapperSpy as NativeLibraryWrapperService)); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "decodequotedprintable"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('decodequotedprintable'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native quoted printable decoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     decodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(decodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class QuotedPrintableDecoder implements Converter { | export class QuotedPrintableDecoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										37
									
								
								src/app/converter/quoted-printable-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/quoted-printable-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {NativeLibraryWrapperService, QuotedPrintable} from '../native-library-wrapper.service'; | ||||||
|  | import {QuotedPrintableEncoder} from './quoted-printable-encoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('QuotedPrintableEncoder', () => { | ||||||
|  |   let sut: QuotedPrintableEncoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let encodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {quotedPrintable: createSpyObj<QuotedPrintable>(['encode'])}; | ||||||
|  |     encodeSpy = nativeWrapperSpy.quotedPrintable!.encode as Spy; | ||||||
|  |     sut = new QuotedPrintableEncoder(nativeWrapperSpy as NativeLibraryWrapperService); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "encodequotedprintable"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('encodequotedprintable'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native quoted printable encoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     encodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(encodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class QuotedPrintableEncoder implements Converter { | export class QuotedPrintableEncoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										28
									
								
								src/app/converter/rot13-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/app/converter/rot13-converter.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | import {ROT13Converter} from './rot13-converter'; | ||||||
|  | 
 | ||||||
|  | describe('ROT13Converter', () => { | ||||||
|  |   let sut: ROT13Converter; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new ROT13Converter()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "rot13"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('rot13'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should encode "Hello, World!" to "Uryyb, Jbeyq!"', () => { | ||||||
|  |     expect(sut.convert('Hello, World!')).toEqual('Uryyb, Jbeyq!'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should return the original input after being applied twice', () => { | ||||||
|  |     const input = 'Ok, so this string is just a bunch of letters. And numbers: 1, 2, 3. Ans others: /&%. Kthxbye!'; | ||||||
|  |     expect(sut.convert(sut.convert(input))).toEqual(input); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -6,14 +6,14 @@ export class ROT13Converter implements Converter { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   getId(): string { |   getId(): string { | ||||||
|     return 'rot13convert'; |     return 'rot13'; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   convert(input: string): string { |   convert(input: string): string { | ||||||
|     try { |     try { | ||||||
|       const inChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |       const inChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; | ||||||
|       const outChars = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'; |       const outChars = 'NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm'; | ||||||
|       const translate = c => { |       const translate = (c: string) => { | ||||||
|         const charIndex = inChars.indexOf(c); |         const charIndex = inChars.indexOf(c); | ||||||
|         return charIndex > -1 ? outChars[charIndex] : c; |         return charIndex > -1 ? outChars[charIndex] : c; | ||||||
|       }; |       }; | ||||||
							
								
								
									
										25
									
								
								src/app/converter/uricomponent-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/converter/uricomponent-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | import {URIComponentDecoder} from './uricomponent-decoder'; | ||||||
|  | 
 | ||||||
|  | describe('URIComponentDecoder', () => { | ||||||
|  |   let sut: URIComponentDecoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new URIComponentDecoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "uricomponentdecode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('uricomponentdecode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should decode "http%3A%2F%2Fm%C3%A4nu%3Agh%C3%ABim%40host%3Aport%2Fhi.there%3Foh%3Dwell%23ya" ' + | ||||||
|  |     'to "http://mänu:ghëim@host:port/hi.there?oh=well#ya"', () => { | ||||||
|  |     expect(sut.convert('http%3A%2F%2Fm%C3%A4nu%3Agh%C3%ABim%40host%3Aport%2Fhi.there%3Foh%3Dwell%23ya')) | ||||||
|  |       .toEqual('http://mänu:ghëim@host:port/hi.there?oh=well#ya'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										25
									
								
								src/app/converter/uricomponent-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/converter/uricomponent-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | import {URIComponentEncoder} from './uricomponent-encoder'; | ||||||
|  | 
 | ||||||
|  | describe('URIComponentEncoder', () => { | ||||||
|  |   let sut: URIComponentEncoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new URIComponentEncoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "uricomponentencode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('uricomponentencode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should encode "http://mänu:ghëim@host:port/hi.there?oh=well#ya" ' + | ||||||
|  |     'to "http%3A%2F%2Fm%C3%A4nu%3Agh%C3%ABim%40host%3Aport%2Fhi.there%3Foh%3Dwell%23ya"', () => { | ||||||
|  |     expect(sut.convert('http://mänu:ghëim@host:port/hi.there?oh=well#ya')) | ||||||
|  |       .toEqual('http%3A%2F%2Fm%C3%A4nu%3Agh%C3%ABim%40host%3Aport%2Fhi.there%3Foh%3Dwell%23ya'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										25
									
								
								src/app/converter/uridecoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/converter/uridecoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | import {URIDecoder} from './uridecoder'; | ||||||
|  | 
 | ||||||
|  | describe('URIDecoder', () => { | ||||||
|  |   let sut: URIDecoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new URIDecoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "uridecode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('uridecode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should decode "http://m%C3%A4nu:gh%C3%ABim@host:port/hi.there?oh=well#ya" ' + | ||||||
|  |     'to "http://mänu:ghëim@host:port/hi.there?oh=well#ya"', () => { | ||||||
|  |     expect(sut.convert('http://m%C3%A4nu:gh%C3%ABim@host:port/hi.there?oh=well#ya')) | ||||||
|  |       .toEqual('http://mänu:ghëim@host:port/hi.there?oh=well#ya'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										25
									
								
								src/app/converter/uriencoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/app/converter/uriencoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,25 @@ | ||||||
|  | import {URIEncoder} from './uriencoder'; | ||||||
|  | 
 | ||||||
|  | describe('URIEncoder', () => { | ||||||
|  |   let sut: URIEncoder; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => sut = new URIEncoder()); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "uriencode"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('uriencode'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should encode "http://mänu:ghëim@host:port/hi.there?oh=well#ya" ' + | ||||||
|  |     'to "http://m%C3%A4nu:gh%C3%ABim@host:port/hi.there?oh=well#ya"', () => { | ||||||
|  |     expect(sut.convert('http://mänu:ghëim@host:port/hi.there?oh=well#ya')) | ||||||
|  |       .toEqual('http://m%C3%A4nu:gh%C3%ABim@host:port/hi.there?oh=well#ya'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										37
									
								
								src/app/converter/utf8-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/utf8-decoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {NativeLibraryWrapperService, Utf8} from '../native-library-wrapper.service'; | ||||||
|  | import {UTF8Decoder} from './utf8-decoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('UTF8Decoder', () => { | ||||||
|  |   let sut: UTF8Decoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let decodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {utf8: createSpyObj<Utf8>(['decode'])}; | ||||||
|  |     decodeSpy = nativeWrapperSpy.utf8!.decode as Spy; | ||||||
|  |     sut = new UTF8Decoder((nativeWrapperSpy as NativeLibraryWrapperService)); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "decodeutf8"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('decodeutf8'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native UTF-8 decoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     decodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(decodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class UTF8Decoder implements Converter { | export class UTF8Decoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										37
									
								
								src/app/converter/utf8-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/converter/utf8-encoder.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {NativeLibraryWrapperService, Utf8} from '../native-library-wrapper.service'; | ||||||
|  | import {UTF8Encoder} from './utf8-encoder'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | import Spy = jasmine.Spy; | ||||||
|  | 
 | ||||||
|  | describe('UTF8Encoder', () => { | ||||||
|  |   let sut: UTF8Encoder; | ||||||
|  |   let nativeWrapperSpy: Partial<NativeLibraryWrapperService>; | ||||||
|  |   let encodeSpy: Spy; | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     nativeWrapperSpy = {utf8: createSpyObj<Utf8>(['encode'])}; | ||||||
|  |     encodeSpy = nativeWrapperSpy.utf8!.encode as Spy; | ||||||
|  |     sut = new UTF8Encoder(nativeWrapperSpy as NativeLibraryWrapperService); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create an instance', () => { | ||||||
|  |     expect(sut).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have a display name', () => { | ||||||
|  |     expect(sut.getDisplayname()).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the id "encodeutf8"', () => { | ||||||
|  |     expect(sut.getId()).toEqual('encodeutf8'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call through to the native UTF-8 encoder', () => { | ||||||
|  |     const testInput = 'My input'; | ||||||
|  |     const expectedOutput = 'It worked'; | ||||||
|  |     encodeSpy.and.returnValue(expectedOutput); | ||||||
|  |     const result: string = sut.convert(testInput); | ||||||
|  |     expect(result).toEqual(expectedOutput); | ||||||
|  |     expect(encodeSpy).toHaveBeenCalledWith(testInput); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import {Converter} from './converter'; | import {Converter} from './converter'; | ||||||
| import {NativeLibraryWrapperService} from '../nativelibrarywrapper.service'; | import {NativeLibraryWrapperService} from '../native-library-wrapper.service'; | ||||||
| 
 | 
 | ||||||
| export class UTF8Encoder implements Converter { | export class UTF8Encoder implements Converter { | ||||||
| 
 | 
 | ||||||
							
								
								
									
										3
									
								
								src/app/error-message/error-message.component.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/app/error-message/error-message.component.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | @if (step.error) { | ||||||
|  |   <div class="errormessage" [innerHTML]="step.message"></div> | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								src/app/error-message/error-message.component.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/error-message/error-message.component.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | .errormessage { | ||||||
|  |   color: red; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								src/app/error-message/error-message.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/app/error-message/error-message.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | import {ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {ErrorMessageComponent} from './error-message.component'; | ||||||
|  | import {Component, ViewChild} from '@angular/core'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   template: '<app-error-message [step]="step"></app-error-message>' | ||||||
|  | }) | ||||||
|  | class TestHostComponent { | ||||||
|  |   public step: Step = new Step(42); | ||||||
|  |   @ViewChild(ErrorMessageComponent) | ||||||
|  |   public sutComponent!: ErrorMessageComponent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('ErrorMessageComponent', () => { | ||||||
|  |   let testHostComponent: TestHostComponent; | ||||||
|  |   let testHostFixture: ComponentFixture<TestHostComponent>; | ||||||
|  | 
 | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await TestBed.configureTestingModule({ | ||||||
|  |       imports: [ErrorMessageComponent], | ||||||
|  |       declarations: [TestHostComponent] | ||||||
|  |     }) | ||||||
|  |       .compileComponents(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     testHostFixture = TestBed.createComponent(TestHostComponent); | ||||||
|  |     testHostComponent = testHostFixture.componentInstance; | ||||||
|  |     testHostFixture.detectChanges(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create', () => { | ||||||
|  |     expect(testHostComponent.sutComponent).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should display an error message', () => { | ||||||
|  |     // arrange
 | ||||||
|  |     testHostComponent.step.error = true; | ||||||
|  |     testHostComponent.step.message = 'This is an error message'; | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     testHostFixture.detectChanges(); | ||||||
|  | 
 | ||||||
|  |     // assert
 | ||||||
|  |     expect(testHostComponent.sutComponent.step.error).toBeTruthy(); | ||||||
|  |     expect(testHostComponent.sutComponent.step.message).toEqual('This is an error message'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										16
									
								
								src/app/error-message/error-message.component.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/app/error-message/error-message.component.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | import {Component, Input} from '@angular/core'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |     selector: 'app-error-message', | ||||||
|  |     templateUrl: './error-message.component.html', | ||||||
|  |     standalone: true, | ||||||
|  |     styleUrls: ['./error-message.component.scss'] | ||||||
|  | }) | ||||||
|  | export class ErrorMessageComponent { | ||||||
|  |   @Input() | ||||||
|  |   public step!: Step; | ||||||
|  | 
 | ||||||
|  |   constructor() { | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								src/app/input-component-manager.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/app/input-component-manager.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | import {inject, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {InputComponentManagerService} from './input-component-manager.service'; | ||||||
|  | import {Step} from './step'; | ||||||
|  | 
 | ||||||
|  | describe('InputComponentManagerService', () => { | ||||||
|  |   beforeEach(() => { | ||||||
|  |     TestBed.configureTestingModule({ | ||||||
|  |       providers: [InputComponentManagerService] | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should be created', inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |     expect(service).toBeTruthy(); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should create a component if requesting the first one', | ||||||
|  |     inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |       const firstStep: Step = service.getFirst(); | ||||||
|  |       expect(firstStep).toBeTruthy(); | ||||||
|  |       expect(service.getFirst()).toBe(firstStep); | ||||||
|  |       expect(service.getAllComponents().length).toEqual(1); | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   it('must not add a null Step', inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |     expect(() => service.register(null!)).toThrowError(); | ||||||
|  |     expect(service.getAllComponents().length).toEqual(0); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('must not add an undefined Step', inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |     expect(() => service.register(undefined!)).toThrowError(); | ||||||
|  |     expect(service.getAllComponents().length).toEqual(0); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should register new Steps', inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |     service.register(new Step(99)); | ||||||
|  |     expect(service.getAllComponents().length).toEqual(1); | ||||||
|  |     service.register(new Step(100)); | ||||||
|  |     expect(service.getAllComponents().length).toEqual(2); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should return a new step if the next step doesn\'t exist', | ||||||
|  |     inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |       // arrange
 | ||||||
|  |       const firstStep: Step = new Step(0); | ||||||
|  |       service.register(firstStep); | ||||||
|  | 
 | ||||||
|  |       // act
 | ||||||
|  |       const nextStep: Step = service.getNext(firstStep); | ||||||
|  | 
 | ||||||
|  |       // assert
 | ||||||
|  |       expect(service.getAllComponents().length).toEqual(2); | ||||||
|  |       expect(nextStep.index).toEqual(1); | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   it('should return the next step if requested', inject([InputComponentManagerService], (service: InputComponentManagerService) => { | ||||||
|  |     // arrange
 | ||||||
|  |     const firstStep: Step = new Step(0); | ||||||
|  |     const nextStep: Step = new Step(1); | ||||||
|  |     service.register(firstStep); | ||||||
|  |     service.register(nextStep); | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     const requestedStep: Step = service.getNext(firstStep); | ||||||
|  |     // assert
 | ||||||
|  |     expect(service.getAllComponents().length).toEqual(2); | ||||||
|  |     expect(requestedStep).toEqual(nextStep); | ||||||
|  |   })); | ||||||
|  | }); | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| import {Injectable} from '@angular/core'; | import {Injectable} from '@angular/core'; | ||||||
| import {Step} from './step'; | import {Step} from './step'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable({ | ||||||
|  |   providedIn: 'root' | ||||||
|  | }) | ||||||
| export class InputComponentManagerService { | export class InputComponentManagerService { | ||||||
|   private components: Step[] = []; |   private components: Step[] = []; | ||||||
| 
 | 
 | ||||||
|  | @ -9,6 +11,9 @@ export class InputComponentManagerService { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public register(component: Step): void { |   public register(component: Step): void { | ||||||
|  |     if (!component) { | ||||||
|  |       throw new Error('component to add must not be empty or undefined'); | ||||||
|  |     } | ||||||
|     this.components.push(component); |     this.components.push(component); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										33
									
								
								src/app/native-library-wrapper.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/app/native-library-wrapper.service.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | import {inject, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {NativeLibraryWrapperService} from './native-library-wrapper.service'; | ||||||
|  | 
 | ||||||
|  | describe('NativeLibraryWrapperService', () => { | ||||||
|  |   beforeEach(() => { | ||||||
|  |     TestBed.configureTestingModule({ | ||||||
|  |       providers: [NativeLibraryWrapperService] | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should be created', inject([NativeLibraryWrapperService], (service: NativeLibraryWrapperService) => { | ||||||
|  |     expect(service).toBeTruthy(); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should convert punycode', inject([NativeLibraryWrapperService], (service: NativeLibraryWrapperService) => { | ||||||
|  |     expect(service.punycode).toBeTruthy(); | ||||||
|  |     expect(service.punycode.encode('bärneruhr')).toEqual('brneruhr-0za'); | ||||||
|  |     expect(service.punycode.decode('brneruhr-0za')).toEqual('bärneruhr'); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should convert utf8', inject([NativeLibraryWrapperService], (service: NativeLibraryWrapperService) => { | ||||||
|  |     expect(service.utf8).toBeTruthy(); | ||||||
|  |     expect(service.utf8.encode('bärneruhr')).toEqual('bärneruhr'); | ||||||
|  |     expect(service.utf8.decode('bärneruhr')).toEqual('bärneruhr'); | ||||||
|  |   })); | ||||||
|  | 
 | ||||||
|  |   it('should convert quoted printable', inject([NativeLibraryWrapperService], (service: NativeLibraryWrapperService) => { | ||||||
|  |     expect(service.quotedPrintable).toBeTruthy(); | ||||||
|  |     expect(service.quotedPrintable.encode('bärneruhr')).toEqual('b=E4rneruhr'); | ||||||
|  |     expect(service.quotedPrintable.decode('b=E4rneruhr')).toEqual('bärneruhr'); | ||||||
|  |   })); | ||||||
|  | }); | ||||||
							
								
								
									
										37
									
								
								src/app/native-library-wrapper.service.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/app/native-library-wrapper.service.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import {Injectable} from '@angular/core'; | ||||||
|  | import * as NativePunycode from 'punycode/'; | ||||||
|  | import * as NativeQuotedPrintable from 'quoted-printable'; | ||||||
|  | import * as NativeUtf8 from 'utf8'; | ||||||
|  | 
 | ||||||
|  | @Injectable({ | ||||||
|  |   providedIn: 'root' | ||||||
|  | }) | ||||||
|  | export class NativeLibraryWrapperService { | ||||||
|  |   public readonly utf8: Utf8; | ||||||
|  |   public readonly quotedPrintable: QuotedPrintable; | ||||||
|  |   public readonly punycode: Punycode; | ||||||
|  | 
 | ||||||
|  |   constructor() { | ||||||
|  |     this.utf8 = NativeUtf8; | ||||||
|  |     this.quotedPrintable = NativeQuotedPrintable; | ||||||
|  |     this.punycode = NativePunycode; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface Punycode { | ||||||
|  |   encode(input: string): string; | ||||||
|  | 
 | ||||||
|  |   decode(input: string): string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface QuotedPrintable { | ||||||
|  |   encode(input: string): string; | ||||||
|  | 
 | ||||||
|  |   decode(input: string): string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface Utf8 { | ||||||
|  |   encode(input: any): string; | ||||||
|  | 
 | ||||||
|  |   decode(input: string): any; | ||||||
|  | } | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| import {Injectable} from '@angular/core'; |  | ||||||
| import {Punycode} from './punycode'; |  | ||||||
| import {Utf8} from './utf8'; |  | ||||||
| import {QuotedPrintable} from './quotedprintable'; |  | ||||||
| import * as NativeUtf8 from 'utf8'; |  | ||||||
| import * as NativeQuotedPrintable from 'quoted-printable'; |  | ||||||
| import * as NativePunycode from 'punycode'; |  | ||||||
| 
 |  | ||||||
| @Injectable() |  | ||||||
| export class NativeLibraryWrapperService { |  | ||||||
|   public utf8: Utf8; |  | ||||||
|   public quotedPrintable: QuotedPrintable; |  | ||||||
|   public punycode: Punycode; |  | ||||||
| 
 |  | ||||||
|   constructor() { |  | ||||||
|     this.utf8 = NativeUtf8; |  | ||||||
|     this.quotedPrintable = NativeQuotedPrintable; |  | ||||||
|     this.punycode = NativePunycode; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| export interface Punycode { |  | ||||||
|   encode(input: string): string; |  | ||||||
|   decode(input: string): string; |  | ||||||
| } |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| export interface QuotedPrintable { |  | ||||||
|   encode(input: string): string; |  | ||||||
|   decode(input: string): string; |  | ||||||
| } |  | ||||||
|  | @ -2,7 +2,7 @@ import {Converter} from './converter/converter'; | ||||||
| 
 | 
 | ||||||
| export class Step { | export class Step { | ||||||
|   public content = ''; |   public content = ''; | ||||||
|   public selectedConverter: Converter = undefined; |   public selectedConverter: Converter | undefined; | ||||||
|   public index: number; |   public index: number; | ||||||
|   public error = false; |   public error = false; | ||||||
|   public message = ''; |   public message = ''; | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								src/app/text-input-field/text-input-field.component.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/app/text-input-field/text-input-field.component.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | <div class="textwrapper arrow_box"> | ||||||
|  |     <textarea class="textinput" (keyup)="update(step)" placeholder="Please enter your input ..." | ||||||
|  |               [(ngModel)]="step.content">{{step.content}}</textarea> | ||||||
|  | </div> | ||||||
							
								
								
									
										69
									
								
								src/app/text-input-field/text-input-field.component.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/app/text-input-field/text-input-field.component.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,69 @@ | ||||||
|  | .textwrapper { | ||||||
|  |   margin: 0 0 1em 0; | ||||||
|  |   padding: 0 1em 0 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .arrow_box { | ||||||
|  |   position: relative; | ||||||
|  |   background: #fff; | ||||||
|  |   border: 1px solid #aaa; | ||||||
|  |   &:focus { | ||||||
|  |     border-color: #888; | ||||||
|  |   } | ||||||
|  |   &:hover { | ||||||
|  |     border-color: #333; | ||||||
|  |   } | ||||||
|  |   &:after, &:before { | ||||||
|  |     top: 100%; | ||||||
|  |     left: 50%; | ||||||
|  |     border: solid transparent; | ||||||
|  |     content: " "; | ||||||
|  |     height: 0; | ||||||
|  |     width: 0; | ||||||
|  |     position: absolute; | ||||||
|  |     pointer-events: none; | ||||||
|  |   } | ||||||
|  |   &:after { | ||||||
|  |     border-color: rgba(255, 255, 255, 0); | ||||||
|  |     border-top-color: #fff; | ||||||
|  |     border-width: 1em; | ||||||
|  |     margin-left: -1em; | ||||||
|  |   } | ||||||
|  |   &:before { | ||||||
|  |     border-color: rgba(170, 170, 170, 0); | ||||||
|  |     border-top-color: #aaa; | ||||||
|  |     border-width: calc(1em + 1px); | ||||||
|  |     margin-left: calc(-1em - 1px); | ||||||
|  |   } | ||||||
|  |   &:focus:before { | ||||||
|  |     border-color: rgba(136, 136, 136, 0); | ||||||
|  |     border-top-color: #888; | ||||||
|  |   } | ||||||
|  |   &:hover:before { | ||||||
|  |     border-color: rgba(51, 51, 51, 0); | ||||||
|  |     border-top-color: #333; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .textinput { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border: none; | ||||||
|  |   color: #000; | ||||||
|  |   font-family: "Free Monospaced", monospace; | ||||||
|  |   height: 10em; | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 0.5em; | ||||||
|  |   resize: vertical; | ||||||
|  |   width: 100%; | ||||||
|  |   &:focus { | ||||||
|  |     border-color: #888; | ||||||
|  |   } | ||||||
|  |   &:hover { | ||||||
|  |     border-color: #333; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .errormessage { | ||||||
|  |   color: red; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
							
								
								
									
										109
									
								
								src/app/text-input-field/text-input-field.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/app/text-input-field/text-input-field.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,109 @@ | ||||||
|  | import {ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {TextInputFieldComponent} from './text-input-field.component'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | import {Component, ViewChild} from '@angular/core'; | ||||||
|  | import {FormsModule} from '@angular/forms'; | ||||||
|  | import {InputComponentManagerService} from '../input-component-manager.service'; | ||||||
|  | import createSpyObj = jasmine.createSpyObj; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   template: '<app-text-input-field [step]="step"></app-text-input-field>' | ||||||
|  | }) | ||||||
|  | class TestHostComponent { | ||||||
|  |   public step: Step = new Step(42); | ||||||
|  |   @ViewChild(TextInputFieldComponent) | ||||||
|  |   public sutComponent!: TextInputFieldComponent; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | describe('TextInputFieldComponent', () => { | ||||||
|  |   let testHostComponent: TestHostComponent; | ||||||
|  |   let testHostFixture: ComponentFixture<TestHostComponent>; | ||||||
|  | 
 | ||||||
|  |   const inputComponentManagerServiceStub = createSpyObj(['getNext']); | ||||||
|  |   inputComponentManagerServiceStub.getNext.and.returnValue(undefined); | ||||||
|  | 
 | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await TestBed.configureTestingModule({ | ||||||
|  |       declarations: [ | ||||||
|  |         TestHostComponent | ||||||
|  |       ], | ||||||
|  |       imports: [TextInputFieldComponent, FormsModule], | ||||||
|  |       providers: [{provide: InputComponentManagerService, useValue: inputComponentManagerServiceStub}] | ||||||
|  |     }) | ||||||
|  |       .compileComponents(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     testHostFixture = TestBed.createComponent(TestHostComponent); | ||||||
|  |     testHostComponent = testHostFixture.componentInstance; | ||||||
|  |     testHostFixture.detectChanges(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create the component', () => { | ||||||
|  |     expect(testHostComponent.sutComponent).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should not perform an update if no converter is selected', () => { | ||||||
|  |     // arrange
 | ||||||
|  |     const step: Step = testHostComponent.step; | ||||||
|  |     step.content = 'Don\'t you change me!'; | ||||||
|  |     step.error = false; | ||||||
|  |     step.message = 'There be no message.'; | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     testHostComponent.sutComponent.update(step); | ||||||
|  | 
 | ||||||
|  |     // assert
 | ||||||
|  |     expect(step.content).toEqual('Don\'t you change me!'); | ||||||
|  |     expect(step.error).toBeFalsy(); | ||||||
|  |     expect(step.message).toEqual('There be no message.'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should set the error flag if the conversion fails', () => { | ||||||
|  |     // arrange
 | ||||||
|  |     const step: Step = testHostComponent.step; | ||||||
|  |     step.content = 'Don\'t you change me!'; | ||||||
|  |     step.error = false; | ||||||
|  |     step.message = 'There be no message.'; | ||||||
|  |     step.selectedConverter = { | ||||||
|  |       getId: () => 'testConverter', | ||||||
|  |       convert: () => { | ||||||
|  |         throw new Error('Test error'); | ||||||
|  |       }, | ||||||
|  |       getDisplayname: () => 'Testing converter' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     testHostComponent.sutComponent.update(step); | ||||||
|  | 
 | ||||||
|  |     // assert
 | ||||||
|  |     expect(step.content).toEqual('Don\'t you change me!'); | ||||||
|  |     expect(step.error).toBeTruthy(); | ||||||
|  |     expect(step.message).toEqual('Test error'); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should correctly convert valid input', () => { | ||||||
|  |     // arrange
 | ||||||
|  |     const nextStep: Step = new Step(82); | ||||||
|  |     inputComponentManagerServiceStub.getNext.and.returnValue(nextStep); | ||||||
|  |     const step: Step = testHostComponent.step; | ||||||
|  |     step.content = 'Don\'t you change me!'; | ||||||
|  |     step.error = true; | ||||||
|  |     step.message = 'There be no message.'; | ||||||
|  |     step.selectedConverter = { | ||||||
|  |       getId: () => 'testConverter', | ||||||
|  |       convert: () => 'The Converted Result', | ||||||
|  |       getDisplayname: () => 'Testing converter' | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // act
 | ||||||
|  |     testHostComponent.sutComponent.update(step); | ||||||
|  | 
 | ||||||
|  |     // assert
 | ||||||
|  |     expect(step.content).toEqual('Don\'t you change me!'); | ||||||
|  |     expect(step.error).toBeFalsy(); | ||||||
|  |     expect(step.message).toEqual(''); | ||||||
|  |     expect(nextStep.content).toEqual('The Converted Result'); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										48
									
								
								src/app/text-input-field/text-input-field.component.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/app/text-input-field/text-input-field.component.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | import {Component, Input} from '@angular/core'; | ||||||
|  | import {Step} from '../step'; | ||||||
|  | import {Converter} from '../converter/converter'; | ||||||
|  | import {InputComponentManagerService} from '../input-component-manager.service'; | ||||||
|  | import {FormsModule} from '@angular/forms'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-text-input-field', | ||||||
|  |   templateUrl: './text-input-field.component.html', | ||||||
|  |   standalone: true, | ||||||
|  |   styleUrls: ['./text-input-field.component.scss'], | ||||||
|  |   imports: [FormsModule] | ||||||
|  | }) | ||||||
|  | export class TextInputFieldComponent { | ||||||
|  |   @Input() | ||||||
|  |   public step!: Step; | ||||||
|  | 
 | ||||||
|  |   constructor(private inputComponentManagerService: InputComponentManagerService) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   update(step: Step): void { | ||||||
|  |     const converter: Converter | undefined = step.selectedConverter; | ||||||
|  | 
 | ||||||
|  |     if (converter !== undefined) { | ||||||
|  |       const content: string = step.content; | ||||||
|  |       let result: string | null; | ||||||
|  |       try { | ||||||
|  |         result = converter.convert(content); | ||||||
|  |       } catch (error) { | ||||||
|  |         if (typeof console === 'object' && typeof console.log === 'function') { | ||||||
|  |           console.log(error); | ||||||
|  |         } | ||||||
|  |         step.message = (error as Error).message; | ||||||
|  |         step.error = true; | ||||||
|  |         result = null; | ||||||
|  |       } | ||||||
|  |       if (result !== null) { | ||||||
|  |         step.message = ''; | ||||||
|  |         step.error = false; | ||||||
|  |         if (result !== '') { | ||||||
|  |           const nextComponent: Step = this.inputComponentManagerService.getNext(step); | ||||||
|  |           nextComponent.content = result; | ||||||
|  |           this.update(nextComponent); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| export interface Utf8 { |  | ||||||
|   encode(input: any): string; |  | ||||||
|   decode(input: string): any; |  | ||||||
| } |  | ||||||
							
								
								
									
										1
									
								
								src/app/version/version.component.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/version/version.component.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | <div [ngClass]="{dev: !PROD}">Version: {{VERSION}}</div> | ||||||
							
								
								
									
										10
									
								
								src/app/version/version.component.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/app/version/version.component.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | div { | ||||||
|  |   font-size: smaller; | ||||||
|  |   color: gray; | ||||||
|  |   display: block; | ||||||
|  |   text-align: right; | ||||||
|  |   margin-right: 2em; | ||||||
|  |   &.dev { | ||||||
|  |     color: red; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/app/version/version.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/app/version/version.component.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | import {ComponentFixture, TestBed} from '@angular/core/testing'; | ||||||
|  | 
 | ||||||
|  | import {VersionComponent} from './version.component'; | ||||||
|  | import packageInfo from '../../../package.json'; | ||||||
|  | 
 | ||||||
|  | describe('VersionComponent', () => { | ||||||
|  |   let component: VersionComponent; | ||||||
|  |   let fixture: ComponentFixture<VersionComponent>; | ||||||
|  | 
 | ||||||
|  |   beforeEach(async () => { | ||||||
|  |     await TestBed.configureTestingModule({ | ||||||
|  |       imports: [VersionComponent] | ||||||
|  |     }) | ||||||
|  |       .compileComponents(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   beforeEach(() => { | ||||||
|  |     fixture = TestBed.createComponent(VersionComponent); | ||||||
|  |     component = fixture.componentInstance; | ||||||
|  |     fixture.detectChanges(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should create the component', () => { | ||||||
|  |     expect(component).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should contain the correct version', () => { | ||||||
|  |     // when executing the test, we're always running with the dev environment
 | ||||||
|  |     expect(component.VERSION).toEqual(`${packageInfo.version} (development build)`); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have the correct value for the "production" property', () => { | ||||||
|  |     // when executing the test, we're always running with the dev environment
 | ||||||
|  |     expect(component.PROD).toBeFalsy(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										18
									
								
								src/app/version/version.component.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/app/version/version.component.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | import {Component, isDevMode} from '@angular/core'; | ||||||
|  | import {CommonModule} from '@angular/common'; | ||||||
|  | import packageInfo from '../../../package.json'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-version', | ||||||
|  |   templateUrl: './version.component.html', | ||||||
|  |   standalone: true, | ||||||
|  |   styleUrls: ['./version.component.scss'], | ||||||
|  |   imports: [CommonModule] | ||||||
|  | }) | ||||||
|  | export class VersionComponent { | ||||||
|  |   public readonly PROD: boolean = !isDevMode(); | ||||||
|  |   public readonly VERSION: string = packageInfo.version + (this.PROD ? '' : ' (development build)'); | ||||||
|  | 
 | ||||||
|  |   constructor() { | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers |  | ||||||
| # For additional information regarding the format and rule options, please see: |  | ||||||
| # https://github.com/browserslist/browserslist#queries |  | ||||||
| # |  | ||||||
| # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed |  | ||||||
| 
 |  | ||||||
| > 0.5% |  | ||||||
| last 2 versions |  | ||||||
| Firefox ESR |  | ||||||
| not dead |  | ||||||
| not IE 9-11 |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| export const environment = { |  | ||||||
|   production: true |  | ||||||
| }; |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| // This file can be replaced during build by using the `fileReplacements` array.
 |  | ||||||
| // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
 |  | ||||||
| // The list of file replacements can be found in `angular.json`.
 |  | ||||||
| 
 |  | ||||||
| export const environment = { |  | ||||||
|   production: false |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
|  * In development mode, for easier debugging, you can ignore zone related error |  | ||||||
|  * stack frames such as `zone.run`/`zoneDelegate.invokeTask` by importing the |  | ||||||
|  * below file. Don't forget to comment it out in production mode |  | ||||||
|  * because it will have a performance impact when errors are thrown |  | ||||||
|  */ |  | ||||||
| // import 'zone.js/dist/zone-error';  // Included with Angular CLI.
 |  | ||||||
|  | @ -1,31 +0,0 @@ | ||||||
| // Karma configuration file, see link for more information
 |  | ||||||
| // https://karma-runner.github.io/1.0/config/configuration-file.html
 |  | ||||||
| 
 |  | ||||||
| module.exports = function (config) { |  | ||||||
|   config.set({ |  | ||||||
|     basePath: '', |  | ||||||
|     frameworks: ['jasmine', '@angular-devkit/build-angular'], |  | ||||||
|     plugins: [ |  | ||||||
|       require('karma-jasmine'), |  | ||||||
|       require('karma-chrome-launcher'), |  | ||||||
|       require('karma-jasmine-html-reporter'), |  | ||||||
|       require('karma-coverage-istanbul-reporter'), |  | ||||||
|       require('@angular-devkit/build-angular/plugins/karma') |  | ||||||
|     ], |  | ||||||
|     client: { |  | ||||||
|       clearContext: false // leave Jasmine Spec Runner output visible in browser
 |  | ||||||
|     }, |  | ||||||
|     coverageIstanbulReporter: { |  | ||||||
|       dir: require('path').join(__dirname, '../coverage'), |  | ||||||
|       reports: ['html', 'lcovonly'], |  | ||||||
|       fixWebpackSourcePaths: true |  | ||||||
|     }, |  | ||||||
|     reporters: ['progress', 'kjhtml'], |  | ||||||
|     port: 9876, |  | ||||||
|     colors: true, |  | ||||||
|     logLevel: config.LOG_INFO, |  | ||||||
|     autoWatch: true, |  | ||||||
|     browsers: ['Chrome'], |  | ||||||
|     singleRun: false |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
							
								
								
									
										16
									
								
								src/main.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								src/main.ts
									
										
									
									
									
								
							|  | @ -1,12 +1,6 @@ | ||||||
| import { enableProdMode } from '@angular/core'; | import { bootstrapApplication } from '@angular/platform-browser'; | ||||||
| import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; | import { appConfig } from './app/app.config'; | ||||||
|  | import { AppComponent } from './app/app.component'; | ||||||
| 
 | 
 | ||||||
| import { AppModule } from './app/app.module'; | bootstrapApplication(AppComponent, appConfig) | ||||||
| import { environment } from './environments/environment'; |   .catch((err) => console.error(err)); | ||||||
| 
 |  | ||||||
| if (environment.production) { |  | ||||||
|   enableProdMode(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| platformBrowserDynamic().bootstrapModule(AppModule) |  | ||||||
|   .catch(err => console.log(err)); |  | ||||||
|  |  | ||||||
|  | @ -1,76 +0,0 @@ | ||||||
| /** |  | ||||||
|  * This file includes polyfills needed by Angular and is loaded before the app. |  | ||||||
|  * You can add your own extra polyfills to this file. |  | ||||||
|  * |  | ||||||
|  * This file is divided into 2 sections: |  | ||||||
|  *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. |  | ||||||
|  *   2. Application imports. Files imported after ZoneJS that should be loaded before your main |  | ||||||
|  *      file. |  | ||||||
|  * |  | ||||||
|  * The current setup is for so-called "evergreen" browsers; the last versions of browsers that |  | ||||||
|  * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), |  | ||||||
|  * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. |  | ||||||
|  * |  | ||||||
|  * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
 |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /*************************************************************************************************** |  | ||||||
|  * BROWSER POLYFILLS |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| /** IE9, IE10 and IE11 requires all of the following polyfills. **/ |  | ||||||
| // import 'core-js/es6/symbol';
 |  | ||||||
| // import 'core-js/es6/object';
 |  | ||||||
| // import 'core-js/es6/function';
 |  | ||||||
| // import 'core-js/es6/parse-int';
 |  | ||||||
| // import 'core-js/es6/parse-float';
 |  | ||||||
| // import 'core-js/es6/number';
 |  | ||||||
| // import 'core-js/es6/math';
 |  | ||||||
| // import 'core-js/es6/string';
 |  | ||||||
| // import 'core-js/es6/date';
 |  | ||||||
| // import 'core-js/es6/array';
 |  | ||||||
| // import 'core-js/es6/regexp';
 |  | ||||||
| // import 'core-js/es6/map';
 |  | ||||||
| // import 'core-js/es6/weak-map';
 |  | ||||||
| // import 'core-js/es6/set';
 |  | ||||||
| 
 |  | ||||||
| /** IE10 and IE11 requires the following for NgClass support on SVG elements */ |  | ||||||
| // import 'classlist.js';  // Run `npm install --save classlist.js`.
 |  | ||||||
| 
 |  | ||||||
| /** IE10 and IE11 requires the following for the Reflect API. */ |  | ||||||
| import 'core-js/es6/reflect'; |  | ||||||
| /** Evergreen browsers require these. **/ |  | ||||||
| // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
 |  | ||||||
| import 'core-js/es7/reflect'; |  | ||||||
| /*************************************************************************************************** |  | ||||||
|  * Zone JS is required by default for Angular itself. |  | ||||||
|  */ |  | ||||||
| import 'zone.js/dist/zone'; // Included with Angular CLI.
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Web Animations `@angular/platform-browser/animations` |  | ||||||
|  * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. |  | ||||||
|  * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). |  | ||||||
|  **/ |  | ||||||
| // import 'web-animations-js';  // Run `npm install --save web-animations-js`.
 |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * By default, zone.js will patch all possible macroTask and DomEvents |  | ||||||
|  * user can disable parts of macroTask/DomEvents patch by setting following flags |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 |  | ||||||
| // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 |  | ||||||
| // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 |  | ||||||
| 
 |  | ||||||
| /* |  | ||||||
| * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js |  | ||||||
| * with the following flag, it will bypass `zone.js` patch for IE/Edge |  | ||||||
| */ |  | ||||||
| // (window as any).__Zone_enable_cross_context_check = true;
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /*************************************************************************************************** |  | ||||||
|  * APPLICATION IMPORTS |  | ||||||
|  */ |  | ||||||
							
								
								
									
										20
									
								
								src/test.ts
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								src/test.ts
									
										
									
									
									
								
							|  | @ -1,20 +0,0 @@ | ||||||
| // This file is required by karma.conf.js and loads recursively all the .spec and framework files
 |  | ||||||
| 
 |  | ||||||
| import 'zone.js/dist/zone-testing'; |  | ||||||
| import { getTestBed } from '@angular/core/testing'; |  | ||||||
| import { |  | ||||||
|   BrowserDynamicTestingModule, |  | ||||||
|   platformBrowserDynamicTesting |  | ||||||
| } from '@angular/platform-browser-dynamic/testing'; |  | ||||||
| 
 |  | ||||||
| declare const require: any; |  | ||||||
| 
 |  | ||||||
| // First, initialize the Angular testing environment.
 |  | ||||||
| getTestBed().initTestEnvironment( |  | ||||||
|   BrowserDynamicTestingModule, |  | ||||||
|   platformBrowserDynamicTesting() |  | ||||||
| ); |  | ||||||
| // Then we find all the tests.
 |  | ||||||
| const context = require.context('./', true, /\.spec\.ts$/); |  | ||||||
| // And load the modules.
 |  | ||||||
| context.keys().map(context); |  | ||||||
|  | @ -1,11 +0,0 @@ | ||||||
| { |  | ||||||
|   "extends": "../tsconfig.json", |  | ||||||
|   "compilerOptions": { |  | ||||||
|     "outDir": "../out-tsc/app", |  | ||||||
|     "types": [] |  | ||||||
|   }, |  | ||||||
|   "exclude": [ |  | ||||||
|     "test.ts", |  | ||||||
|     "**/*.spec.ts" |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
|  | @ -1,18 +0,0 @@ | ||||||
| { |  | ||||||
|   "extends": "../tsconfig.json", |  | ||||||
|   "compilerOptions": { |  | ||||||
|     "outDir": "../out-tsc/spec", |  | ||||||
|     "types": [ |  | ||||||
|       "jasmine", |  | ||||||
|       "node" |  | ||||||
|     ] |  | ||||||
|   }, |  | ||||||
|   "files": [ |  | ||||||
|     "test.ts", |  | ||||||
|     "polyfills.ts" |  | ||||||
|   ], |  | ||||||
|   "include": [ |  | ||||||
|     "**/*.spec.ts", |  | ||||||
|     "**/*.d.ts" |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| { |  | ||||||
|     "extends": "../tslint.json", |  | ||||||
|     "rules": { |  | ||||||
|         "directive-selector": [ |  | ||||||
|             true, |  | ||||||
|             "attribute", |  | ||||||
|             "app", |  | ||||||
|             "camelCase" |  | ||||||
|         ], |  | ||||||
|         "component-selector": [ |  | ||||||
|             true, |  | ||||||
|             "element", |  | ||||||
|             "app", |  | ||||||
|             "kebab-case" |  | ||||||
|         ] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
							
								
								
									
										16
									
								
								tsconfig.app.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tsconfig.app.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | /* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||||||
|  | { | ||||||
|  |   "extends": "./tsconfig.json", | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "outDir": "./out-tsc/app", | ||||||
|  |     "types": [ | ||||||
|  |       "node" | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   "files": [ | ||||||
|  |     "src/main.ts" | ||||||
|  |   ], | ||||||
|  |   "include": [ | ||||||
|  |     "src/**/*.d.ts" | ||||||
|  |   ] | ||||||
|  | } | ||||||
|  | @ -1,21 +1,35 @@ | ||||||
|  | /* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||||||
| { | { | ||||||
|   "compileOnSave": false, |   "compileOnSave": false, | ||||||
|   "compilerOptions": { |   "compilerOptions": { | ||||||
|     "baseUrl": "./", |  | ||||||
|     "outDir": "./dist/out-tsc", |     "outDir": "./dist/out-tsc", | ||||||
|  |     "forceConsistentCasingInFileNames": true, | ||||||
|  |     "strict": true, | ||||||
|  |     "noImplicitOverride": true, | ||||||
|  |     "noPropertyAccessFromIndexSignature": true, | ||||||
|  |     "noImplicitReturns": true, | ||||||
|  |     "noFallthroughCasesInSwitch": true, | ||||||
|  |     "skipLibCheck": true, | ||||||
|  |     "esModuleInterop": true, | ||||||
|     "sourceMap": true, |     "sourceMap": true, | ||||||
|     "declaration": false, |     "declaration": false, | ||||||
|     "module": "es2015", |  | ||||||
|     "moduleResolution": "node", |  | ||||||
|     "emitDecoratorMetadata": true, |  | ||||||
|     "experimentalDecorators": true, |     "experimentalDecorators": true, | ||||||
|     "target": "es5", |     "moduleResolution": "node", | ||||||
|     "typeRoots": [ |     "importHelpers": true, | ||||||
|       "node_modules/@types" |     "target": "ES2022", | ||||||
|     ], |     "module": "ES2022", | ||||||
|  |     "useDefineForClassFields": false, | ||||||
|     "lib": [ |     "lib": [ | ||||||
|       "es2017", |       "ES2022", | ||||||
|       "dom" |       "dom" | ||||||
|     ] |     ], | ||||||
|  |     "resolveJsonModule": true, | ||||||
|  |     "allowSyntheticDefaultImports": true | ||||||
|  |   }, | ||||||
|  |   "angularCompilerOptions": { | ||||||
|  |     "enableI18nLegacyMessageIdFormat": false, | ||||||
|  |     "strictInjectionParameters": true, | ||||||
|  |     "strictInputAccessModifiers": true, | ||||||
|  |     "strictTemplates": true | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								tsconfig.spec.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tsconfig.spec.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | /* To learn more about this file see: https://angular.io/config/tsconfig. */ | ||||||
|  | { | ||||||
|  |   "extends": "./tsconfig.json", | ||||||
|  |   "compilerOptions": { | ||||||
|  |     "outDir": "./out-tsc/spec", | ||||||
|  |     "types": [ | ||||||
|  |       "jasmine" | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   "include": [ | ||||||
|  |     "src/**/*.spec.ts", | ||||||
|  |     "src/**/*.d.ts" | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										130
									
								
								tslint.json
									
										
									
									
									
								
							
							
						
						
									
										130
									
								
								tslint.json
									
										
									
									
									
								
							|  | @ -1,130 +0,0 @@ | ||||||
| { |  | ||||||
|   "rulesDirectory": [ |  | ||||||
|     "node_modules/codelyzer" |  | ||||||
|   ], |  | ||||||
|   "rules": { |  | ||||||
|     "arrow-return-shorthand": true, |  | ||||||
|     "callable-types": true, |  | ||||||
|     "class-name": true, |  | ||||||
|     "comment-format": [ |  | ||||||
|       true, |  | ||||||
|       "check-space" |  | ||||||
|     ], |  | ||||||
|     "curly": true, |  | ||||||
|     "deprecation": { |  | ||||||
|       "severity": "warn" |  | ||||||
|     }, |  | ||||||
|     "eofline": true, |  | ||||||
|     "forin": true, |  | ||||||
|     "import-blacklist": [ |  | ||||||
|       true, |  | ||||||
|       "rxjs/Rx" |  | ||||||
|     ], |  | ||||||
|     "import-spacing": true, |  | ||||||
|     "indent": [ |  | ||||||
|       true, |  | ||||||
|       "spaces" |  | ||||||
|     ], |  | ||||||
|     "interface-over-type-literal": true, |  | ||||||
|     "label-position": true, |  | ||||||
|     "max-line-length": [ |  | ||||||
|       true, |  | ||||||
|       140 |  | ||||||
|     ], |  | ||||||
|     "member-access": false, |  | ||||||
|     "member-ordering": [ |  | ||||||
|       true, |  | ||||||
|       { |  | ||||||
|         "order": [ |  | ||||||
|           "static-field", |  | ||||||
|           "instance-field", |  | ||||||
|           "static-method", |  | ||||||
|           "instance-method" |  | ||||||
|         ] |  | ||||||
|       } |  | ||||||
|     ], |  | ||||||
|     "no-arg": true, |  | ||||||
|     "no-bitwise": true, |  | ||||||
|     "no-console": [ |  | ||||||
|       true, |  | ||||||
|       "debug", |  | ||||||
|       "info", |  | ||||||
|       "time", |  | ||||||
|       "timeEnd", |  | ||||||
|       "trace" |  | ||||||
|     ], |  | ||||||
|     "no-construct": true, |  | ||||||
|     "no-debugger": true, |  | ||||||
|     "no-duplicate-super": true, |  | ||||||
|     "no-empty": false, |  | ||||||
|     "no-empty-interface": true, |  | ||||||
|     "no-eval": true, |  | ||||||
|     "no-inferrable-types": [ |  | ||||||
|       true, |  | ||||||
|       "ignore-params" |  | ||||||
|     ], |  | ||||||
|     "no-misused-new": true, |  | ||||||
|     "no-non-null-assertion": true, |  | ||||||
|     "no-shadowed-variable": true, |  | ||||||
|     "no-string-literal": false, |  | ||||||
|     "no-string-throw": true, |  | ||||||
|     "no-switch-case-fall-through": true, |  | ||||||
|     "no-trailing-whitespace": true, |  | ||||||
|     "no-unnecessary-initializer": true, |  | ||||||
|     "no-unused-expression": true, |  | ||||||
|     "no-use-before-declare": true, |  | ||||||
|     "no-var-keyword": true, |  | ||||||
|     "object-literal-sort-keys": false, |  | ||||||
|     "one-line": [ |  | ||||||
|       true, |  | ||||||
|       "check-open-brace", |  | ||||||
|       "check-catch", |  | ||||||
|       "check-else", |  | ||||||
|       "check-whitespace" |  | ||||||
|     ], |  | ||||||
|     "prefer-const": true, |  | ||||||
|     "quotemark": [ |  | ||||||
|       true, |  | ||||||
|       "single" |  | ||||||
|     ], |  | ||||||
|     "radix": true, |  | ||||||
|     "semicolon": [ |  | ||||||
|       true, |  | ||||||
|       "always" |  | ||||||
|     ], |  | ||||||
|     "triple-equals": [ |  | ||||||
|       true, |  | ||||||
|       "allow-null-check" |  | ||||||
|     ], |  | ||||||
|     "typedef-whitespace": [ |  | ||||||
|       true, |  | ||||||
|       { |  | ||||||
|         "call-signature": "nospace", |  | ||||||
|         "index-signature": "nospace", |  | ||||||
|         "parameter": "nospace", |  | ||||||
|         "property-declaration": "nospace", |  | ||||||
|         "variable-declaration": "nospace" |  | ||||||
|       } |  | ||||||
|     ], |  | ||||||
|     "unified-signatures": true, |  | ||||||
|     "variable-name": false, |  | ||||||
|     "whitespace": [ |  | ||||||
|       true, |  | ||||||
|       "check-branch", |  | ||||||
|       "check-decl", |  | ||||||
|       "check-operator", |  | ||||||
|       "check-separator", |  | ||||||
|       "check-type" |  | ||||||
|     ], |  | ||||||
|     "no-output-on-prefix": true, |  | ||||||
|     "use-input-property-decorator": true, |  | ||||||
|     "use-output-property-decorator": true, |  | ||||||
|     "use-host-property-decorator": true, |  | ||||||
|     "no-input-rename": true, |  | ||||||
|     "no-output-rename": true, |  | ||||||
|     "use-life-cycle-interface": true, |  | ||||||
|     "use-pipe-transform-interface": true, |  | ||||||
|     "component-class-suffix": true, |  | ||||||
|     "directive-class-suffix": true |  | ||||||
|   } |  | ||||||
| } |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue