diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..0f9ccffe6 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,13 @@ +name: Unit Tests # name of the test + +on: [push] # the Github Action will activate "on" the event that you "push" to the repo + +jobs: # the things being executed + tests: # the name of your status check, will become important when you do branch protection + runs-on: ubuntu-latest # which device on Github's server that you are running the Actions on + steps: + - uses: actions/checkout@v4 # using version 4 of Actions + - name: Install Dependencies + run: npm install + - name: Unit Test + run: npm test ./__tests__/sum.test.js # the actual testing line diff --git a/.gitignore b/.gitignore index 496ee2ca6..646ac519e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +node_modules/ diff --git a/README.md b/README.md index f211f6f26..b565dbe10 100644 --- a/README.md +++ b/README.md @@ -1 +1,20 @@ # Lab 5 - Starter + +Members: David De Santiago - A17001592 + +- My "expose.js" [here](https://ddesantiag0.github.io/Lab5_Starter/expose.html) +- My "explore.js" [here](https://ddesantiag0.github.io/Lab5_Starter/explore.html) + +## Check Your Understanding + +### Question 1 +Would you use a unit test to test the “message” feature of a messaging application? Why or why not? For this question, assume the “message” feature allows a user to write and send a message to another user. + +**Answer:** +Unit testing might not be the best choice for testing the entire "message" feature due to its reliance on multiple interacting components. However, unit tests are suitable for testing individual parts of the feature such as the message formatting and validation functions. + +### Question 2 +Would you use a unit test to test the “max message length” feature of a messaging application? Why or why not? For this question, assume the “max message length” feature prevents the user from typing more than 80 characters. + +**Answer:** +Yes, unit testing is appropriate for the "max message length" feature. This feature's functionality is isolated and can be precisely defined and tested without external dependencies, making it ideal for unit testing. diff --git a/__tests__/sum.test.js b/__tests__/sum.test.js index a98ab36e7..e5618b256 100644 --- a/__tests__/sum.test.js +++ b/__tests__/sum.test.js @@ -1,5 +1,11 @@ // sum.test.js +import { sum } from '../code-to-unit-test/sum'; test('adds 1 + 2 to equal 3', () => { // TODO + expect(sum(1 + 2)).toBe(3); }); + +test('adds 1 + 2 to equal 3', () => { + expect(sum(1,2)).toBe(3); //should be wrong +}); \ No newline at end of file diff --git a/__tests__/unit.test.js b/__tests__/unit.test.js index 118a476e0..b1f2bac6d 100644 --- a/__tests__/unit.test.js +++ b/__tests__/unit.test.js @@ -9,3 +9,82 @@ import { } from '../code-to-unit-test/unit-test-me'; // TODO - Part 2 +// Tests for isPhoneNumber +describe('isPhoneNumber', () => { + test('validates correct phone number format', () => { + expect(isPhoneNumber('123-456-7890')).toBe(true); + }); + test('validates another correct phone number format', () => { + expect(isPhoneNumber('(123) 456-7890')).toBe(true); + }); + test('rejects incorrect phone number without dashes', () => { + expect(isPhoneNumber('1234567890')).toBe(false); + }); + test('rejects incorrect phone number with too few digits', () => { + expect(isPhoneNumber('123-45-6789')).toBe(false); + }); +}); + +// Tests for isEmail +describe('isEmail', () => { + test('validates correct email format', () => { + expect(isEmail('test@example.com')).toBe(true); + }); + test('validates another correct email format', () => { + expect(isEmail('hello.world@domain.co.uk')).toBe(true); + }); + test('rejects missing domain email', () => { + expect(isEmail('test@')).toBe(false); + }); + test('rejects email without "@" symbol', () => { + expect(isEmail('testexample.com')).toBe(false); + }); +}); + +// Tests for isStrongPassword +describe('isStrongPassword', () => { + test('validates strong password with special characters', () => { + expect(isStrongPassword('Example123!')).toBe(true); + }); + test('validates strong password with underscore', () => { + expect(isStrongPassword('Strong_Pass1')).toBe(true); + }); + test('rejects weak password with fewer characters', () => { + expect(isStrongPassword('examp')).toBe(false); + }); + test('rejects password without numbers or special characters', () => { + expect(isStrongPassword('WeakPassword')).toBe(false); + }); +}); + +// Tests for isDate +describe('isDate', () => { + test('validates correct date format MM/DD/YYYY', () => { + expect(isDate('04/12/2021')).toBe(true); + }); + test('validates correct date format MM/D/YYYY', () => { + expect(isDate('04/2/2021')).toBe(true); + }); + test('rejects incorrect date format with month name', () => { + expect(isDate('April 12, 2021')).toBe(false); + }); + test('rejects incorrect format without year', () => { + expect(isDate('04/12')).toBe(false); + }); +}); + +// Tests for isHexColor +describe('isHexColor', () => { + test('validates correct 6-digit hex color', () => { + expect(isHexColor('#FFFFFF')).toBe(true); + }); + test('validates correct 3-digit hex color', () => { + expect(isHexColor('#FFF')).toBe(true); + }); + test('rejects hex color without #', () => { + expect(isHexColor('FFFFFF')).toBe(false); + }); + test('rejects incorrect hex color with non-hex characters', () => { + expect(isHexColor('#ZZZZZZ')).toBe(false); + }); +}); diff --git a/assets/scripts/explore.js b/assets/scripts/explore.js index 777f5ee3a..cea7faec7 100644 --- a/assets/scripts/explore.js +++ b/assets/scripts/explore.js @@ -1,7 +1,53 @@ // explore.js +// resources: https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis -window.addEventListener('DOMContentLoaded', init); +window.addEventListener("DOMContentLoaded", init); function init() { - // TODO -} \ No newline at end of file + const synth = window.speechSynthesis; + const textArea = document.getElementById('text-to-speak'); + const voiceSelect = document.getElementById('voice-select'); + const speakButton = document.querySelector('button'); + const faceImage = document.querySelector('img'); + let voices = []; + + function populateVoices() { + voices = synth.getVoices(); + voiceSelect.innerHTML = ''; + voices.forEach((voice) => { + const option = document.createElement('option'); + option.textContent = `${voice.name} (${voice.lang})${voice.default ? ' — DEFAULT' : ''}`; + option.setAttribute('data-name', voice.name); + option.setAttribute('data-lang', voice.lang); + voiceSelect.appendChild(option); + }); + } + + populateVoices(); + if (synth.onvoiceschanged !== undefined) { + synth.onvoiceschanged = populateVoices; + } + + function speak() { + if (synth.speaking) { + console.error('speechSynthesis.speaking'); + return; + } + if (textArea.value !== '') { + const utterance = new SpeechSynthesisUtterance(textArea.value); + const selectedVoiceName = voiceSelect.selectedOptions[0].getAttribute('data-name'); + utterance.voice = voices.find(voice => voice.name === selectedVoiceName); + + utterance.onstart = () => { + faceImage.src = 'assets/images/smiling-open.png'; + }; + utterance.onend = () => { + faceImage.src = 'assets/images/smiling.png'; + }; + + synth.speak(utterance); + } + } + + speakButton.addEventListener('click', speak); +} diff --git a/assets/scripts/expose.js b/assets/scripts/expose.js index 962d7a33c..972d8d0dd 100644 --- a/assets/scripts/expose.js +++ b/assets/scripts/expose.js @@ -3,5 +3,58 @@ window.addEventListener('DOMContentLoaded', init); function init() { - // TODO + // Get references to DOM elements + const hornSelectElement = document.getElementById('horn-select'); + const volumeSliderElement = document.getElementById('volume'); + const playButtonElement = document.querySelector('button'); + const audioElement = document.querySelector('audio'); + const hornImageElement = document.querySelector('#expose img'); + const volumeIconElement = document.querySelector('#volume-controls img'); + + // Event listener for horn selection + hornSelectElement.addEventListener('change', function() { + const selectedHorn = hornSelectElement.value; + hornImageElement.src = `assets/images/${selectedHorn}.svg`; + hornImageElement.alt = selectedHorn; + audioElement.src = `assets/audio/${selectedHorn}.mp3`; + }); + + // Event listener for volume slider + volumeSliderElement.addEventListener('input', function() { + const volumeValue = volumeSliderElement.value; + audioElement.volume = volumeValue / 100; + + if (volumeValue == 0) { + volumeIconElement.src = 'assets/icons/volume-level-0.svg'; + volumeIconElement.alt = 'Volume level 0'; + } else if (volumeValue < 33) { + volumeIconElement.src = 'assets/icons/volume-level-1.svg'; + volumeIconElement.alt = 'Volume level 1'; + } else if (volumeValue < 67) { + volumeIconElement.src = 'assets/icons/volume-level-2.svg'; + volumeIconElement.alt = 'Volume level 2'; + } else { + volumeIconElement.src = 'assets/icons/volume-level-3.svg'; + volumeIconElement.alt = 'Volume level 3'; + } + }); + + // Event listener for play button + playButtonElement.addEventListener('click', function() { + audioElement.play(); + if (hornSelectElement.value === 'party-horn') { + const jsConfetti = new JSConfetti(); + jsConfetti.addConfetti({ + confettiRadius: 6, + confettiNumber: 300, + spread: 360, + startVelocity: 50, + gravity: 1, + origin: { + x: 0.5, + y: 0.5 + } + }); + } + }); } \ No newline at end of file