diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..beaafc9 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,84 @@ +name: Run Tests and Report Results + +on: + push: + branches: + - main + +jobs: + grade-assignment: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install NPM dependencies ๐Ÿšš + run: npm install + + - name: STUDENT, FIND YOUR TEST RESULTS HERE! ๐Ÿงช + run: | + set +e # Prevent script from stopping on errors + npx jest mvp.test.js --json --outputFile=results.json + exit_code=$? + echo "ERROR_FLAG=$exit_code" >> $GITHUB_ENV + set -e # Re-enable script stopping on errors + shell: bash + + - name: Process test results โš™๏ธ + if: always() # This ensures that this step runs even if the previous step fails + run: | + num_passed_tests=0 + num_total_tests=1 # Default to 1 to avoid divide by zero + if [ -f results.json ]; then + num_passed_tests=$(jq '.numPassedTests' results.json) + num_total_tests=$(jq '.numTotalTests' results.json) + if [ "$num_total_tests" -eq 0 ]; then + num_total_tests=1 + fi + fi + ratio=$(echo "scale=2; $num_passed_tests / $num_total_tests" | bc) + echo "PERCENTAGE=$ratio" >> $GITHUB_ENV + shell: bash + + - name: Post results to API ๐Ÿ“ค + run: | + commit_hash=$(git rev-parse HEAD) + commit_timestamp=$(git show -s --format=%cI HEAD) # ISO 8601 format + commit_email=$(git show -s --format=%ce HEAD) + commit_message=$(git log -1 --pretty=%B) # Fetch the last commit message + repo_name="${GITHUB_REPOSITORY#*/}" + json_payload=$(jq -n \ + --argjson test_results ${PERCENTAGE} \ + --arg repo_name "$repo_name" \ + --arg actor "${{ github.actor }}" \ + --arg commit_hash "$commit_hash" \ + --arg commit_timestamp "$commit_timestamp" \ + --arg commit_email "$commit_email" \ + --arg commit_message "$commit_message" \ + '{test_results: $test_results, repo_name: $repo_name, actor: $actor, commit_hash: $commit_hash, commit_timestamp: $commit_timestamp, commit_email: $commit_email, commit_message: $commit_message}') + curl -X POST https://webapis.bloomtechdev.com/grades/report \ + -H "Content-Type: application/json" \ + -d "$json_payload" + shell: bash + env: + GITHUB_REPOSITORY: ${{ github.repository }} + PERCENTAGE: ${{ env.PERCENTAGE }} + + - name: Mark workflow failed if not all tests pass + if: always() # Ensure this step runs regardless of previous step outcomes + run: | + test_passed=$(echo "$PERCENTAGE * 100" | bc) + echo "Finished with $(echo "$PERCENTAGE * 100" | bc)% tests passing" + if [ $(echo "$test_passed < 100" | bc) -eq 1 ]; then + echo "Not all tests passed. Marking the workflow run as failed." + exit 1 + else + echo "All tests passed. Workflow run will be marked as successful." + fi + shell: bash + env: + PERCENTAGE: ${{ env.PERCENTAGE }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4953998 --- /dev/null +++ b/.gitignore @@ -0,0 +1,260 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/README.md b/README.md index 4507537..45f05fc 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Sprint 2 Challenge ## Overview -Welcome to the Sprint 2 Challenge! You will be completing a list of coding challenges by writing code in the `index.html` file. Each coding challenge has a set of tests that must pass in order to receive a passing grade. +Welcome to the Sprint 2 Challenge! You will be completing a list of coding challenges by writing code in the `index.html` file. Each coding challenge has a set of tests that must pass in order to receive a passing grade. Below you will find step-by-step instructions to help you get setup and complete this Sprint Challenge. Good luck! ## 1. Setup Instructions ### Cloning the Repository -To get started, clone the repository to your local machine. Cloning creates a local copy of the project on your computer. To clone your repository, open your terminal or Git BASH and execute: +To get started, clone the repository to your local machine. Cloning creates a local copy of the project on your computer. To clone your repository, open your terminal or Git BASH and execute: ``` git clone [your-repo-url-here] @@ -27,7 +27,7 @@ Once you have your files on your machine, open the `index.html` file in two prog ## 2. Challenge Completion ### Working Through Challenges -For each challenge, read the instructions within the challenge link below and write your code in the `index.html` file. The `index.html` file has a function labeled with the challenge number where you will write your code. +For each challenge, read the instructions within the challenge link below and write your code in the `index.html` file. The `index.html` file has a function labeled with the challenge number where you will write your code. You can ignore the rest of the files. - **Challenges List:** - [Challenge 1](./challenges/Challenge_1.md) - [Challenge 2](./challenges/Challenge_2.md) @@ -37,7 +37,7 @@ For each challenge, read the instructions within the challenge link below and wr - [Challenge 6 (optional)](./challenges/Challenge_6.md) ### Testing Your Code -To see if your code is passing the tests, open the `index.html` file in Chrome and look at the console. You will see a green checkbox for each test you pass and a red X for any tests that did not pass. +To see if your code is passing the tests, open the `index.html` file in Chrome and look at the console. You will see a green checkbox for each test you pass and a red X for any tests that did not pass. ![An image of what tests look like when the pass and fail in the Chrome Console](./images/tests-running.png) @@ -53,12 +53,12 @@ Use the following git commands to push your completed challenge to GitHub for au ``` git add . git commit -m 'Challenge Submission' -git push +git push origin main ``` At any point of the Sprint Challenge, you can submit your code to CodeGrade and you can make an unlimited number of submissions. ## Ask Us: We're Here to Support You! -If you find yourself struggling during this Sprint Challenge, don't hesitate to use LearnBot or reach out to an LA or instructor. We know coding can be challenging, especially when you're just starting out. +If you find yourself struggling during this Sprint Challenge, don't hesitate to use LearnBot or reach out to an LA or instructor. We know coding can be challenging, especially when you're just starting out. -Best of luck with your Sprint 2 Challenge! +Have fun with your Sprint 2 Challenge! diff --git a/mvp.test.js b/mvp.test.js new file mode 100644 index 0000000..4ddb62e --- /dev/null +++ b/mvp.test.js @@ -0,0 +1,89 @@ +const { JSDOM } = require('jsdom') +const fs = require('fs') + +let pathToHTML = './index.html' + +const htmlString = fs.readFileSync(pathToHTML, { encoding: 'utf8' }) +const { window } = new JSDOM(htmlString) + +eval(window.document.querySelector('#challenge').textContent) + +describe('Tests', () => { + test('functions and classes exist in script', () => { + expect(() => { + profileActivation + mineSweeper + booleanize + scrub + }).not.toThrow() + }) + describe('CHALLENGE 1 - profileActivation', () => { + { + let expected = 'confirm status manually' + test(`returns "${expected}" when active profile has reason prop`, () => { + expect(profileActivation({ active: true, reason: '' })).toBe(expected) + }) + } + { + test(`given an active profile, returns a deactivated one with a reason`, () => { + expect(profileActivation({ active: true }, 'because')).toEqual({ active: false, reason: 'because' }) + }) + } + { + test(`given an inactive profile, it deletes the reason and returns an active one`, () => { + expect(profileActivation({ active: false, reason: 'because' })).toEqual({ active: true }) + }) + } + }) + describe('CHALLENGE 2 - mineSweeper', () => { + test('invalid coordinates', () => { + expect(mineSweeper([['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฅ'], ['๐ŸŸฆ', '๐ŸŸฅ', '๐ŸŸฅ'], ['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฆ']], 0, 4)).toBe("invalid coordinates") + expect(mineSweeper([['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฅ'], ['๐ŸŸฆ', '๐ŸŸฅ', '๐ŸŸฅ'], ['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฆ']], 0, 1)).toBe("invalid coordinates") + expect(mineSweeper([['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฅ'], ['๐ŸŸฆ', '๐ŸŸฅ', '๐ŸŸฅ'], ['๐ŸŸฅ', '๐ŸŸฆ', '๐ŸŸฆ']], 1, 4)).toBe("invalid coordinates") + expect(mineSweeper([["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฅ"]], 1, 1)).toBe("๐ŸŸฆ ๐Ÿฅณ") + expect(mineSweeper([["๐ŸŸฆ", "๐ŸŸฅ", "๐ŸŸฆ"], ["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"]], 2, 1)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"]], 3, 1)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฆ"]], 1, 2)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฆ", "๐ŸŸฅ", "๐ŸŸฆ"]], 2, 2)).toBe("๐ŸŸฆ ๐Ÿฅณ") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฆ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"]], 3, 2)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฆ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"]], 1, 3)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฅ"]], 2, 3)).toBe("๐ŸŸฆ ๐Ÿฅณ") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฆ", "๐ŸŸฅ", "๐ŸŸฅ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"]], 3, 3)).toBe("๐ŸŸฆ ๐Ÿฅณ") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"]], 1, 1)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"]], 2, 2)).toBe("๐ŸŸฅ ๐Ÿ’€") + expect(mineSweeper([["๐ŸŸฅ", "๐ŸŸฆ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฆ"], ["๐ŸŸฅ", "๐ŸŸฅ", "๐ŸŸฅ"]], 3, 3)).toBe("๐ŸŸฅ ๐Ÿ’€") + }) + }) + describe('CHALLENGE 3 - booleanize', () => { + test('returns object with null props removed', () => { + expect(booleanize({ bad1: null })).toEqual({}) + expect(booleanize({ bad1: null, bad2: null })).toEqual({}) + }) + { + let expected = "shorten all prop names to 9 chars or less" + test(`returns "${expected}" if any key length > 9`, () => { + expect(booleanize({ '0123456789': 1 })).toBe(expected) + }) + } + test('returns object with ones turned to true', () => { + expect(booleanize({ a: 1, b: 1 })).toEqual({ a: true, b: true }) + }) + test('returns object with zeroes turned to false', () => { + expect(booleanize({ a: 0, b: 0 })).toEqual({ a: false, b: false }) + }) + test('performs no unintended changes', () => { + expect(booleanize({ a: 1, b: 0, c: null, d: 'Lady Gaga' })) + .toEqual({ a: true, b: false, d: 'Lady Gaga' }) + }) + }) + describe('CHALLENGE 4 - scrub', () => { + test('replaces forbidden words with words of equal length made of "x" chars', () => { + expect(scrub("out of the silent planet", ["of", "silent"])).toBe("out xx the xxxxxx planet") + expect(scrub("out of the silent planet", ["of", "planet"])).toBe("out xx the silent xxxxxx") + expect(scrub("the ghost of the navigator", ["the"])).toBe("xxx ghost of xxx navigator") + expect(scrub("lost somewhere in time", [])).toBe("lost somewhere in time") + expect(scrub("aces high", ["high", "aces", "hearts"])).toBe("xxxx xxxx") + expect(scrub("", ["high", "aces"])).toBe("") + }) + }) +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..cb74977 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "sprint-2-challenge", + "devDependencies": { + "jest": "29.7.0", + "jsdom": "24.0.0" + } +}