fix(integer-base-converter): handle non-decimal char and better error message
This commit is contained in:
		
							parent
							
								
									0ff853437b
								
							
						
					
					
						commit
						8476cf319b
					
				| @ -7,7 +7,7 @@ export function convertBase({ value, fromBase, toBase }: { value: string; fromBa | |||||||
|     .reverse() |     .reverse() | ||||||
|     .reduce((carry: number, digit: string, index: number) => { |     .reduce((carry: number, digit: string, index: number) => { | ||||||
|       if (!fromRange.includes(digit)) { |       if (!fromRange.includes(digit)) { | ||||||
|         throw new Error('Invalid digit `' + digit + '` for base ' + fromBase + '.'); |         throw new Error('Invalid digit "' + digit + '" for base ' + fromBase + '.'); | ||||||
|       } |       } | ||||||
|       return (carry += fromRange.indexOf(digit) * Math.pow(fromBase, index)); |       return (carry += fromRange.indexOf(digit) * Math.pow(fromBase, index)); | ||||||
|     }, 0); |     }, 0); | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
|       <div v-if="styleStore.isSmallScreen"> |       <div v-if="styleStore.isSmallScreen"> | ||||||
|         <n-input-group> |         <n-input-group> | ||||||
|           <n-input-group-label style="flex: 0 0 120px"> Input number: </n-input-group-label> |           <n-input-group-label style="flex: 0 0 120px"> Input number: </n-input-group-label> | ||||||
|           <n-input-number v-model:value="inputNumber" min="0" style="width: 100%" /> |           <n-input v-model:value="input" style="width: 100%" :status="error ? 'error' : undefined" /> | ||||||
|         </n-input-group> |         </n-input-group> | ||||||
|         <n-input-group> |         <n-input-group> | ||||||
|           <n-input-group-label style="flex: 0 0 120px"> Input base: </n-input-group-label> |           <n-input-group-label style="flex: 0 0 120px"> Input base: </n-input-group-label> | ||||||
| @ -14,51 +14,65 @@ | |||||||
| 
 | 
 | ||||||
|       <n-input-group v-else> |       <n-input-group v-else> | ||||||
|         <n-input-group-label style="flex: 0 0 120px"> Input number: </n-input-group-label> |         <n-input-group-label style="flex: 0 0 120px"> Input number: </n-input-group-label> | ||||||
|         <n-input-number v-model:value="inputNumber" min="0" /> |         <n-input v-model:value="input" :status="error ? 'error' : undefined" /> | ||||||
|         <n-input-group-label style="flex: 0 0 120px"> Input base: </n-input-group-label> |         <n-input-group-label style="flex: 0 0 120px"> Input base: </n-input-group-label> | ||||||
|         <n-input-number v-model:value="inputBase" max="64" min="2" /> |         <n-input-number v-model:value="inputBase" max="64" min="2" /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
|  | 
 | ||||||
|  |       <n-alert v-if="error" style="margin-top: 25px" type="error">{{ error }}</n-alert> | ||||||
|       <n-divider /> |       <n-divider /> | ||||||
| 
 | 
 | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 170px"> Binary (2): </n-input-group-label> |         <n-input-group-label style="flex: 0 0 170px"> Binary (2): </n-input-group-label> | ||||||
|         <input-copyable :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: 2 })" readonly /> |         <input-copyable | ||||||
|  |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 2 })" | ||||||
|  |           readonly | ||||||
|  |           placeholder="Binary version will be here..." | ||||||
|  |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
| 
 | 
 | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 170px"> Octal (8): </n-input-group-label> |         <n-input-group-label style="flex: 0 0 170px"> Octal (8): </n-input-group-label> | ||||||
|         <input-copyable :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: 8 })" readonly /> |         <input-copyable | ||||||
|  |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 8 })" | ||||||
|  |           readonly | ||||||
|  |           placeholder="Octal version will be here..." | ||||||
|  |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
| 
 | 
 | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 170px"> Decimal (10): </n-input-group-label> |         <n-input-group-label style="flex: 0 0 170px"> Decimal (10): </n-input-group-label> | ||||||
|         <input-copyable |         <input-copyable | ||||||
|           :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: 10 })" |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 10 })" | ||||||
|           readonly |           readonly | ||||||
|  |           placeholder="Decimal version will be here..." | ||||||
|         /> |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
| 
 | 
 | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 170px"> Hexadecimal (16): </n-input-group-label> |         <n-input-group-label style="flex: 0 0 170px"> Hexadecimal (16): </n-input-group-label> | ||||||
|         <input-copyable |         <input-copyable | ||||||
|           :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: 16 })" |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 16 })" | ||||||
|           readonly |           readonly | ||||||
|  |           placeholder="Decimal version will be here..." | ||||||
|         /> |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
| 
 | 
 | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 170px"> Base64 (64): </n-input-group-label> |         <n-input-group-label style="flex: 0 0 170px"> Base64 (64): </n-input-group-label> | ||||||
|         <input-copyable |         <input-copyable | ||||||
|           :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: 64 })" |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: 64 })" | ||||||
|           readonly |           readonly | ||||||
|  |           placeholder="Base64 version will be here..." | ||||||
|         /> |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
|       <n-input-group> |       <n-input-group> | ||||||
|         <n-input-group-label style="flex: 0 0 85px"> Custom: </n-input-group-label> |         <n-input-group-label style="flex: 0 0 85px"> Custom: </n-input-group-label> | ||||||
|         <n-input-number v-model:value="outputBase" style="flex: 0 0 86px" max="64" min="2" /> |         <n-input-number v-model:value="outputBase" style="flex: 0 0 86px" max="64" min="2" /> | ||||||
|         <input-copyable |         <input-copyable | ||||||
|           :value="convertBase({ value: String(inputNumber), fromBase: inputBase, toBase: outputBase })" |           :value="errorlessConvert({ value: input, fromBase: inputBase, toBase: outputBase })" | ||||||
|           readonly |           readonly | ||||||
|  |           :placeholder="`Base ${outputBase} will be here...`" | ||||||
|         /> |         /> | ||||||
|       </n-input-group> |       </n-input-group> | ||||||
|     </n-card> |     </n-card> | ||||||
| @ -66,16 +80,31 @@ | |||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref } from 'vue'; | import { computed, ref } from 'vue'; | ||||||
| import { useStyleStore } from '@/stores/style.store'; | import { useStyleStore } from '@/stores/style.store'; | ||||||
|  | import { getErrorMessageIfThrows } from '@/utils/error'; | ||||||
| import { convertBase } from './integer-base-converter.model'; | import { convertBase } from './integer-base-converter.model'; | ||||||
| import InputCopyable from '../../components/InputCopyable.vue'; | import InputCopyable from '../../components/InputCopyable.vue'; | ||||||
| 
 | 
 | ||||||
| const styleStore = useStyleStore(); | const styleStore = useStyleStore(); | ||||||
| 
 | 
 | ||||||
| const inputNumber = ref(42); | const input = ref('42'); | ||||||
| const inputBase = ref(10); | const inputBase = ref(10); | ||||||
| const outputBase = ref(42); | const outputBase = ref(42); | ||||||
|  | 
 | ||||||
|  | function errorlessConvert(...args: Parameters<typeof convertBase>) { | ||||||
|  |   try { | ||||||
|  |     return convertBase(...args); | ||||||
|  |   } catch (err) { | ||||||
|  |     return ''; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const error = computed(() => | ||||||
|  |   getErrorMessageIfThrows(() => | ||||||
|  |     convertBase({ value: input.value, fromBase: inputBase.value, toBase: outputBase.value }), | ||||||
|  |   ), | ||||||
|  | ); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <style lang="less" scoped> | <style lang="less" scoped> | ||||||
|  | |||||||
							
								
								
									
										29
									
								
								src/utils/error.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/utils/error.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | import { describe, expect, it } from 'vitest'; | ||||||
|  | import { getErrorMessageIfThrows } from './error'; | ||||||
|  | 
 | ||||||
|  | describe('error util', () => { | ||||||
|  |   describe('getErrorMessageIfThrows', () => { | ||||||
|  |     it('get an error message if the callback throws, undefined instead', () => { | ||||||
|  |       expect( | ||||||
|  |         getErrorMessageIfThrows(() => { | ||||||
|  |           throw 'message'; | ||||||
|  |         }), | ||||||
|  |       ).to.equal('message'); | ||||||
|  | 
 | ||||||
|  |       expect( | ||||||
|  |         getErrorMessageIfThrows(() => { | ||||||
|  |           throw new Error('message'); | ||||||
|  |         }), | ||||||
|  |       ).to.equal('message'); | ||||||
|  | 
 | ||||||
|  |       expect( | ||||||
|  |         getErrorMessageIfThrows(() => { | ||||||
|  |           throw { message: 'message' }; | ||||||
|  |         }), | ||||||
|  |       ).to.equal('message'); | ||||||
|  | 
 | ||||||
|  |       // eslint-disable-next-line @typescript-eslint/no-empty-function
 | ||||||
|  |       expect(getErrorMessageIfThrows(() => {})).to.equal(undefined); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										24
									
								
								src/utils/error.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/utils/error.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | import _ from 'lodash'; | ||||||
|  | 
 | ||||||
|  | export { getErrorMessageIfThrows }; | ||||||
|  | 
 | ||||||
|  | function getErrorMessageIfThrows(cb: () => unknown) { | ||||||
|  |   try { | ||||||
|  |     cb(); | ||||||
|  |     return undefined; | ||||||
|  |   } catch (err) { | ||||||
|  |     if (_.isString(err)) { | ||||||
|  |       return err; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (_.isError(err)) { | ||||||
|  |       return err.message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (_.isObject(err) && _.has(err, 'message')) { | ||||||
|  |       return (err as { message: string }).message; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 'An error as occurred.'; | ||||||
|  |   } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user