Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing #103

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
14 changes: 14 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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
- run: npm test ./__tests__/unit.test.js
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules/
.DS_Store
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
# Lab 5 - Starter
## Girma Terfa
[Explore Page](https://gir-ma.github.io/Lab5_Starter/explore.html)

[Expose page:](https://gir-ma.github.io/Lab5_Starter/expose.html)

### Check Your Understanding
1. NO, I wouldn't use a unit test in this case (message) for messaging app. The reason is, unit test are for testing individual parts of code in isolation and the message feature interacts between user and systems.
2. Yes, I would use the unit test for testing the max message length because this feature is a specific and isolated funcionality.
9 changes: 9 additions & 0 deletions __tests__/sum.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// sum.test.js

test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
import { sum } from '../code-to-unit-test/sum';
test('adds 1 + 2 to equal 3', () => {
expect(sum(1,2)).toBe(3);
});
64 changes: 64 additions & 0 deletions __tests__/unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// unit.test.js

import {
isPhoneNumber,
isEmail,
isStrongPassword,
isDate,
isHexColor,
} from '../code-to-unit-test/unit-test-me';

// Test cases for isPhoneNumber function
test('isPhoneNumber should return true for valid phone numbers', () => {
expect(isPhoneNumber('(123) 456-7890')).toBe(true);
expect(isPhoneNumber('123-456-7890')).toBe(true);
});

test('isPhoneNumber should return false for invalid phone numbers', () => {
expect(isPhoneNumber('1234')).toBe(false);
expect(isPhoneNumber('123-456-789')).toBe(false);
});

// Test cases for isEmail function
test('isEmail should return true for valid emails', () => {
expect(isEmail('[email protected]')).toBe(true);
expect(isEmail('[email protected]')).toBe(true);
});

test('isEmail should return false for invalid emails', () => {
expect(isEmail('test@example')).toBe(false);
expect(isEmail('user123@gmail')).toBe(false);
});

// Test cases for isStrongPassword function
test('isStrongPassword should return true for strong passwords', () => {
expect(isStrongPassword('Abcd1234')).toBe(true);
expect(isStrongPassword('Pass_word123')).toBe(true);
});

test('isStrongPassword should return false for weak passwords', () => {
expect(isStrongPassword('wek')).toBe(false);
expect(isStrongPassword('123')).toBe(false);
});

// Test cases for isDate function
test('isDate should return true for valid dates', () => {
expect(isDate('12/25/2023')).toBe(true);
expect(isDate('1/1/2024')).toBe(true);
});

test('isDate should return false for invalid dates', () => {
expect(isDate('2023/12/25')).toBe(false);
expect(isDate('25-12-2023')).toBe(false);
});

// Test cases for isHexColor function
test('isHexColor should return true for valid hex colors', () => {
expect(isHexColor('#123abc')).toBe(true);
expect(isHexColor('#fff')).toBe(true);
});

test('isHexColor should return false for invalid hex colors', () => {
expect(isHexColor('#FFGGHH')).toBe(false); // Corrected assertion
expect(isHexColor('#F')).toBe(false);
});
60 changes: 59 additions & 1 deletion assets/scripts/explore.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,63 @@
window.addEventListener('DOMContentLoaded', init);

function init() {
// TODO
// Get DOM elements
var voiceSelector = document.getElementById('voice-select');
var face = document.getElementsByTagName('img')[0];
var talkBtn = document.getElementsByTagName('button')[0];
var inputField = document.getElementById('text-to-speak');

// Initialize SpeechSynthesis
var synth = window.speechSynthesis;
var voices = [];

// Function to populate the voice dropdown
function fillList() {
voices = synth.getVoices();
// Clear previous options
voiceSelector.innerHTML = '';
voices.forEach(function(voice) {
// Create and append options for each voice
var option = document.createElement('option');
option.textContent = voice.name + ' (' + voice.lang + ')';
option.setAttribute('data-name', voice.name);
option.setAttribute('data-lang', voice.lang);
voiceSelector.appendChild(option);
});
}

// Call fillList initially and on voices changed event
fillList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = fillList;
}

// Event listener for "Press to Talk" button
talkBtn.addEventListener('click', function() {
// Change the image source to open mouth
face.src = "assets/images/smiling-open.png";

// Get the selected voice option
var userOption = voiceSelector.selectedOptions[0];
var nameOfOption = userOption.getAttribute('data-name');

// Create SpeechSynthesisUtterance instance with user input
var speechLine = new SpeechSynthesisUtterance(inputField.value);

// Find the selected voice and assign it to speechLine
for(var i = 0; i < voices.length ; i++) {
if(voices[i].name === nameOfOption) {
speechLine.voice = voices[i];
break; // Once voice is found, exit loop
}
}

// Speak the user's text
synth.speak(speechLine);

// Reset face image after speech ends
speechLine.onend = function(event) {
face.src = "assets/images/smiling.png";
};
});
}
95 changes: 94 additions & 1 deletion assets/scripts/expose.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,98 @@
window.addEventListener('DOMContentLoaded', init);

function init() {
// TODO
//for the images
const selectElement = document.getElementById("horn-select");
//for the volume icon
const volumeElement = document.getElementById("volume");
const volumeIcon = document.querySelector("div img");
//for the play button
const playButton = document.querySelector("button");

// Event listener for horn selection change
selectElement.addEventListener('change', (event) => {
const selectedHorn = event.target.value;
const imageElement = document.querySelector("main img");

// Update image source based on selected horn
switch (selectedHorn) {
case "air-horn":
imageElement.src = "assets/images/air-horn.svg";
break;
case "car-horn":
imageElement.src = "assets/images/car-horn.svg";
break;
case "party-horn":
imageElement.src = "assets/images/party-horn.svg";
break;
default:
break;
}
});

volumeElement.addEventListener('input', () => {
const volume = volumeElement.value;

// Update volume icon based on volume level
if (volume == 0) {
volumeIcon.src = "assets/icons/volume-level-0.svg";
} else if (volume < 33) {
volumeIcon.src = "assets/icons/volume-level-1.svg";
} else if (volume < 67) {
volumeIcon.src = "assets/icons/volume-level-2.svg";
} else {
volumeIcon.src = "assets/icons/volume-level-3.svg";
}

// Adjust volume for the audio element
const audioElement = document.querySelector("audio");
audioElement.volume = volume / 100;
});

// Create instances of JSConfetti for Left and right
const jsConfettiLeft = new JSConfetti({
confettiRadius: 4,
confettiNumber: 500,
target: 'left',
});

const jsConfettiRight = new JSConfetti({
confettiRadius: 4,
confettiNumber: 500,
target: 'right',
});
// Event listener for play button click
playButton.onclick = () => {
const selectedHorn = selectElement.value;
const audioElement = document.querySelector("audio");

// Set the audio source based on the selected horn
switch (selectedHorn) {
case "air-horn":
audioElement.src = "assets/audio/air-horn.mp3";
break;
case "car-horn":
audioElement.src = "assets/audio/car-horn.mp3";
break;
case "party-horn":
audioElement.src = "assets/audio/party-horn.mp3";

// Trigger confetti effect for party horn from both top and bottom

jsConfettiLeft.addConfetti({
confettiColors: [
'#ff0a54', '#ff477e', '#ff7096','#ff85a1', '#fbb1bd', '#fbb1bd', '#f9bec7'
],
});
jsConfettiRight.addConfetti({
emojis: ['🌈', '⚡️', '💥', '✨', '💫', '🌸'],
});
break;
default:
break;
}

// Play the selected horn sound
audioElement.play();
};
}
3 changes: 3 additions & 0 deletions code-to-unit-test/sum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function sum(a, b) {
return a + b;
}
32 changes: 32 additions & 0 deletions code-to-unit-test/unit-test-me.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// matches valid phone numbers
export function isPhoneNumber(phoneNumber) {
const phoneRegex = /((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}/;
return phoneRegex.test(phoneNumber);
}

// matches valid emails
export function isEmail(email) {
const emailRegex = /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/;
return emailRegex.test(email);
}

/**
* The password's first character must be a letter, it must contain at least * 4 characters and no more than 15 characters and no characters other than * * letters, numbers and the underscore may be used
*/
export function isStrongPassword(password) {
const pwRegex = /^[a-zA-Z]\w{3,14}$/;
return pwRegex.test(password);
}

// This regular expressions matches dates of the form XX / XX / YYYY where
// XX can be 1 or 2 digits long and YYYY is always 4 digits long.
export function isDate(date) {
const dateRegex = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
return dateRegex.test(date);
}

// Matches valid 3 or 6 character hex codes used for HTML or CSS.
export function isHexColor(color) {
const colorRegex = /^\#?[A-Fa-f0-9]{3}([A-Fa-f0-9]{3})?$/;
return colorRegex.test(color);
}
13 changes: 13 additions & 0 deletions expand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Expand
1. Why is it important to put thought into your IDs & Classes when it comes to technology intersections? (e.g. how HTML, CSS, and JS intersect)
- properly named ID's and classes make it easier to target specific element with CSS and js functionality.
2. What are Data attributes? Why might they be useful? How do you access them? What are the implications of using Data attributes when it comes to things like microdata?
- Data attributes are custom attributes that can be added to HTML elements to store additional information. They are prefixed with data-, followed by a meaningful name.
- can be accessed using dataset; for example, if an element has a data attribute named ```data-id```, you can access its value in JavaScript using ```element.dataset.id```.
- Microdata is a standardized way of annotating HTML elements with specific attributes to describe their meaning and context, improving optimality.
3. What is a DOM fragment? Why are they powerful?
- structure that contains a subset of nodes without being attached to the main DOM tree.
- they are powerful because they offer performance benefits when working with multiple DOM nodes. Instead of directly manipulating the main DOM tree, which can trigger reflows and repaints, we can manipulate nodes within a fragment and then append the entire fragment to the document in one operation.
4. What is the point of a “Virtual DOM”? What do you gain? What do you lose?
5. In JavaScript, usually you can reference every attribute of an element with a dot selector followed by the attribute name, except for the class attribute, which is className. Why is this so?
6. What is the difference between using addEventListener() and something like onClick() ? What are the advantages / disadvantages of both?
Loading