{"id":16007989,"url":"https://github.com/erickzhao/gmail-selenium-test","last_synced_at":"2026-04-16T14:09:34.064Z","repository":{"id":97289570,"uuid":"174586136","full_name":"erickzhao/gmail-selenium-test","owner":"erickzhao","description":"🥒 Cucumber in Agile Story Test Automation","archived":false,"fork":false,"pushed_at":"2019-03-11T01:48:20.000Z","size":619,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-10T10:44:17.260Z","etag":null,"topics":["automation","cucumber","selenium","testing"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erickzhao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-08T18:11:12.000Z","updated_at":"2020-11-09T15:35:33.000Z","dependencies_parsed_at":"2023-06-26T04:15:18.521Z","dependency_job_id":null,"html_url":"https://github.com/erickzhao/gmail-selenium-test","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erickzhao%2Fgmail-selenium-test","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erickzhao%2Fgmail-selenium-test/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erickzhao%2Fgmail-selenium-test/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erickzhao%2Fgmail-selenium-test/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erickzhao","download_url":"https://codeload.github.com/erickzhao/gmail-selenium-test/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247277414,"owners_count":20912523,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","cucumber","selenium","testing"],"created_at":"2024-10-08T12:22:58.880Z","updated_at":"2026-04-16T14:09:34.014Z","avatar_url":"https://github.com/erickzhao.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📨 Gmail Selenium Test\n\n## Background\n\n- Student ID: 260687719\n- Name: Erick Zhao\n\nThis assignment was submitted as Assignment B for [ECSE 428](https://www.mcgill.ca/study/2018-2019/courses/ecse-428) at McGill University in the Winter 2019 semester.\n\n## Table of contents\n\n1. [Story statement and flows](#story-statement)\n2. [Gherkin scripts](#gherkin-scripts)\n3. [Test environment description (approach, dependencies, future improvements, etc. )](#test-environment-description)\n4. [Installation](#installation)\n5. [Running tests](#running-tests)\n6. [Adding new Gherkin scripts and steps](#adding-new-gherkin-scripts-and-steps)\n7. [A note on modularity](#a-note-on-modularity)\n8. [All files delivered](#all-files-delivered)\n9. [Link to video and source code](#links)\n\n## Story statement\n\n### Story\n\n```\nAs a user of the Gmail mail provider,\nI would like to be able to send emails with image attachments\nso I can communicate asynchronously via text and image  to anyone with an internet connection across the world.\n```\n\n### Flows\n\nTo respect the guidelines of this assignment, there is a normal, alternate, and error flow available, as well as 5 different test cases for each flow, which each have a different permutation of recipients and image attachment file types.\n\nA separate scenario outline was used for each one of these flows, with a set of 5 variable combinations included for each one. See the [Gherkin scripts](#gherkin-scripts) below for more details, and the `/lib/users.js` file ([link to GitHub here](https://github.com/erickzhao/gmail-selenium-test/blob/master/lib/users.js#L6)) for each specific email to be used.\n\n#### Normal flow\n\n```\nNormal flow: Sending emails to a valid recipient using files from Computer\nGiven I have an email draft addressed to someone with someone else as a Cc\nAnd I have chosen a single image to be attached from my local computer\nWhen I send the email\nThen I should be alerted that the email was sent successfully\nAnd the draft should no longer be available\nAnd the email should be sent\nAnd the email's details should correspond to the original draft that was sent\n```\n\n#### Alternate flow\n\n```\nScenario Outline: Sending emails to a valid recipient using files from Google Drive\nGiven I have an email draft addressed to someone with someone else as a Cc\nAnd I have chosen a single image to be attached from Google Drive\nWhen I send the email\nThen I should be alerted that the email was sent successfully\nAnd the draft should no longer be available\nAnd the email should be sent\nAnd the email's details should correspond to the original draft that was sent\n```\n\n#### Error flow\n\n```\nError flow: Sending emails with an image attachment to an invalid recipient\nGiven an email draft addressed to someone with someone else as a Cc (with one email invalid)\nAnd I have chosen a single image to be attached from my local computer\nWhen I send the email\nThen the draft should remain open\nAnd I should be warned that the recipients are invalid\nAnd the email should not be sent\n```\n\n## Gherkin scripts\n\nThese scripts are also accessible via the repository in the `/features/send_email.feature` file. ([Link to GitHub here](https://github.com/erickzhao/gmail-selenium-test/blob/master/features/send_email.feature))\n\n```\nFeature: Sending email with image attachment\n\n  Background:\n    Given CurrentUser is logged into the Gmail web client\n    And CurrentUser is composing a new message\n\n  Scenario Outline: Sending emails to a valid recipient using files from Computer (Normal Flow)\n    Given I have an email draft addressed to \u003crecipient\u003e with \u003ccc\u003e as a Cc\n    And I have chosen a single \u003cfiletype\u003e image to be attached from my local computer\n    When I send the email\n    Then I should be alerted that the email was sent successfully\n    And the draft should no longer be available\n    And the email should be sent\n    And the email's details should correspond to the original draft that was sent\n\n    Examples:\n      | recipient     | cc            | filetype |\n      | \"CurrentUser\" | \"OtherUser-1\" | \".png\"   |\n      | \"CurrentUser\" | \"OtherUser-2\" | \".jpg\"   |\n      | \"OtherUser-3\" | \"CurrentUser\" | \".tiff\"  |\n      | \"OtherUser-4\" | \"CurrentUser\" | \".gif\"   |\n      | \"OtherUser-5\" | \"CurrentUser\" | \".svg\"   |\n\n  Scenario Outline: Sending emails to a valid recipient using files from Google Drive (Alternate Flow)\n    Given I have an email draft addressed to \u003crecipient\u003e with \u003ccc\u003e as a Cc\n    And a single \u003cfiletype\u003e image is chosen to be attached from Google Drive\n    When I send the email\n    Then I should be alerted that the email was sent successfully\n    And the draft should no longer be available\n    And the email should be sent\n    And the email's details should correspond to the original draft that was sent\n\n    Examples:\n      | recipient     | cc            | filetype |\n      | \"CurrentUser\" | \"OtherUser-1\" | \".png\"   |\n      | \"CurrentUser\" | \"OtherUser-2\" | \".jpg\"   |\n      | \"OtherUser-3\" | \"CurrentUser\" | \".tiff\"  |\n      | \"OtherUser-4\" | \"CurrentUser\" | \".gif\"   |\n      | \"OtherUser-5\" | \"CurrentUser\" | \".svg\"   |\n\n  Scenario Outline: Sending emails to invalid recipient using files from Computer (Error Flow)\n    Given I have an email draft addressed to \u003crecipient\u003e with \u003ccc\u003e as a Cc\n    And I have chosen a single \u003cfiletype\u003e image to be attached from my local computer\n    When I send the email\n    Then the draft should remain open\n    And I should be warned that the recipients are invalid\n    And the email should not be sent\n    Examples:\n      | recipient       | cc              | filetype |\n      | \"CurrentUser\"   | \"InvalidUser-1\" | \".png\"   |\n      | \"CurrentUser\"   | \"InvalidUser-2\" | \".jpg\"   |\n      | \"InvalidUser-3\" | \"CurrentUser\"   | \".tiff\"  |\n      | \"InvalidUser-4\" | \"CurrentUser\"   | \".gif\"   |\n      | \"InvalidUser-5\" | \"CurrentUser\"   | \".svg\"   |\n```\n\n## Test environment description\n\n### High-level testing approach\n\nMy test approach involved using three Scenario Outlines to mix and match the 3 flows described above with a combination of variables: Recipient email, Cc email, and image attachment type.\n\nFirst, I set up the initial state before each scenario by ensuring login status and navigating to the inbox page. This was done in the `Background` step of my Gherkin feature.\n\nFor each case, I composed a draft with a unique subject and body (with timestamp of when the tests were run), which were saved later for assertions. Then, I sent the email to both another user and myself (I was either the recipient or the Cc in each case).\n\nErgo,since each email was sent to my inbox, I checked both my the Inbox and Sent folders for a subject with a matching timestamp. I then clicked on the email and validated if its details corresponded to my draft (subject, body, sender, recipient, cc, and attachment file name).\n\nAfter each scenario was run, I then cleared my Inbox and Sent folders, as well as discarded any existing drafts, to reset the system to its initial state.\n\n### Dependencies\n\nThis assignment was written in **JavaScript** using the [NodeJS](https://nodejs.org/en/) runtime and the [npm](https://www.npmjs.com/) package manager. The project was built using Selenium Webdriver (with ChromeDriver for running the tests), Cucumber to automate the stories into acceptance tests, and Chai as an assertion library.\n\nHere are the versions of the dependencies specified by the `package.json`:\n\n- `\"chai\": \"^4.2.0\",` - Chai is an BDD assertion library ([See documentation here](https://www.chaijs.com/)).\n- `\"chai-as-promised\": \"^7.1.1\",` - A plugin for Chai to support assertions for asynchronous [JavaScript Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) ([see documentation here](https://www.chaijs.com/plugins/chai-as-promised/)).\n- `\"chromedriver\": \"^2.46.0\",` - npm wrapper used to fetch the corresponding version of ChromeDriver ([see documentation here](https://www.npmjs.com/package/chromedriver)). [ChromeDriver](http://chromedriver.chromium.org/) itself is a standalone server implementing the WebDriver protocol. Used alongside Selenium to run acceptance tests.\n- `\"cucumber\": \"^5.1.0\",` - A JavaScript implementation of the Cucumber tool to run plain language automated tests ([see documentation here](https://www.npmjs.com/package/cucumber)).\n- `\"selenium-webdriver\": \"^4.0.0-alpha.1\"` - JavaScript bindings for the Selenium automation library ([see documentation here](https://seleniumhq.github.io/selenium/docs/api/javascript/)).\n\n_N.B. This notation follows the npm guidelines for semantic versioning ([link here](https://docs.npmjs.com/misc/semver)). In general, the caret (`^`) icon next to any `X.X.X` version means that any `X.X.Y` version downloaded would also be accepted._\n\n### Hardware\n\nAll tests were run on macOS Mojave 10.14.3 with a 2018-model Macbook Pro with a 2.7 GHz Intel Core i7 processor and 16 GB 2133 MHz LPDDR3 RAM.\n\n### Test account\n\nA fresh Gmail account was created for my purposes. Since the alternate flows required attachments from Google Drive, I had upload the five images from the `/images/` folder into my test account's Google Drive.\n\n### Pros and cons of approach taken\n\nA few aspects of my approach had some tradeoffs, which I outline below.\n\n#### Sending emails to myself\n\nThe main peculiarity of my approach was that I always sent the email back to myself. This allowed me to ensure that my email was successfully delivered, since I could check if my test account had recieved the email. This is a more robust check than just inspecting my Sent folder (because there could have been a breakdown between the email going to my outbox and the recipient receiving the email). It also allows me to perform such a check without validating with an external API call to another email's inbox\n\nHowever, this approach does not reflect a very common real-world use-case. People rarely need to send emails to themselves.\n\n#### Checking file validity\n\nTo validate that the image attachment was indeed uploaded correctly, I simply checked if the sent email's attachment had a matching name and extension. This is aneasy way that trusts that the image was not corrupted. However, the best way to see if the attachment was uploaded correctly would be to download it and compare the binary to the original picture. This seemed out of scope to me, though.\n\n#### Teardown\n\nThe teardown step in my code is implemented in `/features/step_definitions/after)steps.js`. It sets a clean slate by deleting all emails sent. This is fine because I'm using a test account. However, to more accurately replicate real-world usage, we would want to only delete the test email that was sent within the scenario.\n\n### Specific locators\n\nThe Selenium locators I used to run the browser automation are specific to the [Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) of the Gmail client, with many relying on XPath axes (ancestor, descendant, etc.) and the inner texts of the elements. This makes the tests more vulnerable to DOM shifts than having specific IDs or CSS classes to target. However, this was probably my best approach given that we were running tests on the production build of Gmail, with the IDs and CSS classes obfuscated and changing with each instance. My approach might have been different had I had access to a development build with legible, static class names and IDs.\n\nUsing inner texts also means that I cannot testing across different localizations of the Gmail client. This was fine since testing in multiple locales was out of the scope of this project, but this is still a vulnerability nonetheless.\n\n### Future improvements (commercial use)\n\nIn the previous section, I discussed some tradeoffs with the robustness of my approach. Indeed, the aforementioned solutions lead to more robust software more suitable for commercial use, but definitely require the additional resources that come with a commercial product:\n\n- Scenarios with only external recipients with an API to tell the acceptance tests that the email was successfully sent.\n- Checking image attachment binaries to ensure validity of the image upload system.\n- Only teardown emails that were sent in the current scenario.\n- Use a development build with HTML DOM attributes that are static and legible.\n- Store string constants to match all locales.\n\n### If the web interface changed\n\nI discussed this in more detail in the section above ([link here](#specific-locators)), but the gist of it is that my approach is fairly vulnerable to changes in the HTML DOM. In its current state of my project, if the UI changed, I would have to rebind all outdated selectors.\n\n## Installation\n\nBefore starting installation, make sure that you have [NodeJS](https://nodejs.org/en/) installed, with the latest LTS version (`v10.15.3`). Also, ensure you have the latest version of npm (`6.4.1`).\n\nTo run this project, first clone the GitHub repository ([link below](#links)). Next, install all packages via command line with:\n\n```\nnpm install\n```\n\nYou should then see a `/node_modules/` folder appear with all dependencies installed. For more information on each NPM dependency, check the `package.json` file or [see the Dependencies section above](#dependencies).\n\n## Running tests\n\nOnce all packages are done installing, run the test command in your command line interface using:\n\n```\nnpm test\n```\n\nThis will execute the Cucumber scripts and run the whole test suite. Your output should look something like this:\n\n```\n[gmail-selenium-test] npm test\n\n\u003e gmail-selenium-test@0.1.0 test /Users/erickzhao/Code/gmail-selenium-test\n\u003e cucumber-js\n\n.................................................................................................................................................\n\n15 scenarios (15 passed)\n130 steps (130 passed)\n0m54.203s\n```\n\n## Adding new Gherkin scripts and steps\n\nTo add new Gherkin scripts to the same feature, append your script to `/features/send_email.feature`. To add additional features, create any `.feature` file within the `/features/` directory.\n\nTo add corresponding steps, add a new `.js` file to `/features/step_definitions/`, or append your steps to the most appropriate existing file:\n\n- `after_steps.js`: teardown (in After hook)\n- `background_steps.js`: setup (in Background or Before hook)\n- `email_steps.js`: outline-specific commands for `send_email.feature`.\n\n## A note on modularity\n\nI did a few things to make my code as modular as possible:\n\n- Split step definitions between setup (`/features/step_definitions/background_steps.js`), teardown (`features/step_definitions/after_steps.js`), and main step code `features/step_definitions/email_steps.js`)\n- Packaged all locators into an easily-accessible Object for reuse (`/lib/locators.js`)\n- Packaged reusable helper functions into their own file (`/lib/utils.js`)\n- Separated dependency configuration logic from step logic (`/lib/chai.js`, `/lib/cucumber.js`, and `/lib/driver.js`)\n- Saved user information into its own constants file (`/lib/users.js`)\n\n## All files delivered\n\nInside the GitHub repository (and this submission), there are the following folders and files\n\n```\n|--features\n|----step_definitions (contains all step definitions)\n|------after_steps.js (contains after hook steps)\n|------background_steps.js (contains background steps)\n|------email_steps (contains email steps)\n|--images (contains all locally hosted images)\n|---- howdy.jpg\n|---- howdy.png\n|---- howdy.tiff\n|---- howdy.svg\n|---- howdy.gif\n|--lib (contains helper code)\n|---- chai.js (Chai config)\n|---- cucumber.js (Cucumber config)\n|---- driver.js (Driver config)\n|---- locators.js (Selenium locator constants)\n|---- users.js (Credentials and Email constants)\n|---- utils.js (Various helper functions)\n|-- .eslintrc.js (JS Linter configuration)\n|-- .gitignore (Ignored files on git)\n|-- package.json (List of all packages and run scripts)\n|-- package-lock.json (Lockfile for dependency versions)\n|-- README.md (Markdown version of this report)\n|-- final-report.pdf (PDF version of this report)\n```\n\n## Links\n\n- 📼 Screen recording: [https://www.youtube.com/watch?v=maewsZztNZU](https://www.youtube.com/watch?v=maewsZztNZU)\n- 👩‍💻 Code repository: [https://github.com/erickzhao/gmail-selenium-test](https://github.com/erickzhao/gmail-selenium-test)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferickzhao%2Fgmail-selenium-test","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferickzhao%2Fgmail-selenium-test","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferickzhao%2Fgmail-selenium-test/lists"}