-
Notifications
You must be signed in to change notification settings - Fork 2
Add Cucumber-JS + Playwright BrowserStack SDK sample #3
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
Merged
karanshah-browserstack
merged 9 commits into
main
from
add-cucumber-js-playwright-sample
Jul 15, 2026
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
3a19f65
Add Cucumber-JS + Playwright BrowserStack SDK sample
saraswati-padmawar c1dbf48
Use @playwright/test so the SDK detects and routes Playwright
saraswati-padmawar 2fe370d
Add sanity workflow (clean-network CI verification on Node 20)
saraswati-padmawar c9a223b
Pin @cucumber/cucumber to ^11 (v12 needs Node 22 fs/promises.glob)
saraswati-padmawar 15d5c10
README: document Node 18/20/22 support + Cucumber v11 pin rationale
saraswati-padmawar 37637b0
README: make the BrowserStack Local test runnable as documented
saraswati-padmawar 799bdfd
ci: pin GitHub Actions to commit SHAs (fixes Semgrep p/default)
saraswati-padmawar ec9bf4e
Fix source tag branch segment to main (per review)
saraswati-padmawar b1d71ee
Fix source tag to <repo>:sample-main:v1.0 per Sample Repo Creation ch…
saraswati-padmawar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Sanity workflow that verifies the Cucumber-JS + Playwright BrowserStack SDK sample | ||
| # on a clean-network GitHub runner, mirroring browserstack/node-js-playwright-browserstack. | ||
| # Runs the public bstackdemo `sample` profile, then the BrowserStack Local `local` | ||
| # profile against a python http.server harness on port 45454. | ||
|
|
||
| name: Cucumber-JS Playwright SDK sanity workflow on workflow_dispatch | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| ref: | ||
| description: 'Branch or commit to build (defaults to the triggering ref)' | ||
| required: false | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| sanity: | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| node: ['20'] | ||
| name: Cucumber-JS Playwright Sample (Node ${{ matrix.node }}) | ||
| env: | ||
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | ||
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.ref || github.ref }} | ||
|
|
||
| - name: Setup node | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | ||
| with: | ||
| node-version: ${{ matrix.node }} | ||
|
|
||
| - name: Strip credential placeholders so env vars take effect | ||
| # The yml ships with literal YOUR_USERNAME / YOUR_ACCESS_KEY placeholders; | ||
| # the SDK uses those literal values unless the lines are absent. | ||
| run: sed -i '/^userName:/d; /^accessKey:/d' browserstack.yml | ||
|
|
||
| - name: Install dependencies | ||
| run: npm install | ||
|
|
||
| - name: Run sample test (public bstackdemo) | ||
| run: npm run sample-test | ||
|
|
||
| - name: Run local test (BrowserStack Local + python http.server harness) | ||
| run: | | ||
| set -u | ||
| mkdir -p "$RUNNER_TEMP/bs-local-harness" | ||
| cat > "$RUNNER_TEMP/bs-local-harness/index.html" <<'HTML' | ||
| <!doctype html> | ||
| <html><head><title>BrowserStack Local Test</title></head> | ||
| <body>OK</body></html> | ||
| HTML | ||
| ( cd "$RUNNER_TEMP/bs-local-harness" && python3 -m http.server 45454 ) & | ||
| HTTP_PID=$! | ||
| trap 'kill "$HTTP_PID" 2>/dev/null || true' EXIT | ||
| sleep 2 | ||
| sed -i 's/^browserstackLocal: false.*/browserstackLocal: true/' browserstack.yml | ||
| npm run sample-local-test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| node_modules | ||
| test-results | ||
| log/ | ||
| package-lock.json | ||
| local.log | ||
| .DS_Store | ||
| browserstack.err |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,100 @@ | ||
| # cucumber-js-playwright-browserstack | ||
| Creating a sample repo for different Playwright languages and runners | ||
|
|
||
| This sample shows how to run [Cucumber-JS](https://github.com/cucumber/cucumber-js) + [Playwright](https://playwright.dev/) tests on BrowserStack using the [BrowserStack Node SDK](https://www.npmjs.com/package/browserstack-node-sdk). The SDK reads `browserstack.yml`, fans your scenarios out across the platforms listed there, starts and stops BrowserStack Local automatically, and reports test status to the BrowserStack dashboard. Your test code stays plain `@playwright/test` + `@cucumber/cucumber` -- no manual `connect()`, no capabilities in code. | ||
|
|
||
|  | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| * [Node.js](https://nodejs.org/) 18, 20, or 22 LTS, and npm (verified on Node 20) | ||
| * A BrowserStack account -- grab your [Username and Access Key](https://www.browserstack.com/accounts/settings) | ||
|
|
||
| > **Note on the Cucumber version:** this sample pins `@cucumber/cucumber` to **v11**, which supports Node 18/20/22. Cucumber **v12** (`latest`) requires **Node 22+** (it uses `fs/promises.glob`) and will fail on Node 18/20 with `promises_1.glob is not a function`. Bump to v12 only if you are on Node 22+. | ||
|
|
||
| ## Setup | ||
|
|
||
| * Clone the repo | ||
| * Install dependencies: | ||
|
|
||
| ```sh | ||
| npm install | ||
| ``` | ||
|
|
||
| * Add your credentials to `browserstack.yml` (replace `YOUR_USERNAME` / `YOUR_ACCESS_KEY`), or remove those two lines and export them as environment variables instead: | ||
|
|
||
| ```sh | ||
| export BROWSERSTACK_USERNAME=<browserstack-username> | ||
| export BROWSERSTACK_ACCESS_KEY=<browserstack-access-key> | ||
| ``` | ||
|
|
||
| ## Run Sample Test | ||
|
|
||
| Runs the public [bstackdemo.com](https://bstackdemo.com/) add-to-cart scenario (the `sample` Cucumber profile) across every platform in `browserstack.yml`: | ||
|
|
||
| ```sh | ||
| npm run sample-test | ||
| ``` | ||
|
|
||
| ## Run Local Test (BrowserStack Local) | ||
|
|
||
| Use this when the site under test is on `localhost`, a staging host, or behind a firewall. BrowserStack Local opens a secure tunnel and resolves `bs-local.com` back to **your** machine, so the cloud browser can reach a page only you can serve. | ||
|
|
||
| The bundled `local` scenario navigates to `http://bs-local.com:45454/` and asserts the page title contains `BrowserStack Local` — so you must have something serving that page on port `45454` first: | ||
|
|
||
| 1. Serve a matching page locally (any page whose `<title>` contains "BrowserStack Local" satisfies the scenario): | ||
|
|
||
| ```sh | ||
| mkdir -p bs-local-site && printf '<!doctype html><title>BrowserStack Local</title><body>OK</body>' > bs-local-site/index.html | ||
| (cd bs-local-site && python3 -m http.server 45454) & | ||
| ``` | ||
|
|
||
| To test **your own** app instead, serve it on port `45454` (or change the port and the title assertion in `features/step_definitions/local_steps.js`). | ||
|
|
||
| 2. Set `browserstackLocal: true` in `browserstack.yml`. | ||
|
|
||
| 3. Run the `local` profile: | ||
|
|
||
| ```sh | ||
| npm run sample-local-test | ||
| ``` | ||
|
|
||
| The SDK starts and stops the BrowserStack Local tunnel for you -- no manual binary download or lifecycle management. The tunnel routes `bs-local.com:45454` to your machine's `localhost:45454`. | ||
|
|
||
| ## How the SDK changes things | ||
|
|
||
| - **One `browserstack.yml`** declares platforms, parallelism, the Local toggle, and reporting; the SDK picks them up automatically. | ||
| - **The SDK runs platforms in parallel for you** -- one Cucumber run per `(platform x parallelsPerPlatform)` cell, no per-platform branching needed. | ||
| - **The SDK rewrites Playwright launches** -- `features/support/hooks.js` calls `chromium.launch()` and the SDK transparently redirects it to the per-platform browser configured in the yml (`chrome` / `playwright-webkit` / `playwright-firefox`). No `chromium.connect(wss_url)` plumbing. | ||
| - **The SDK starts and stops BrowserStack Local** when `browserstackLocal: true`. | ||
|
|
||
| ## Repo layout | ||
|
|
||
| ``` | ||
| . | ||
| ├── browserstack.yml # SDK config: credentials, platforms, Local toggle, reporting | ||
| ├── package.json # SDK run scripts + deps | ||
| ├── cucumber.js # Cucumber profiles: `sample` (bstackdemo) and `local` (bs-local) | ||
| └── features/ | ||
| ├── sample.feature # bstackdemo add-to-cart scenario | ||
| ├── local.feature # BrowserStack Local scenario | ||
| ├── step_definitions/ | ||
| │ ├── sample_steps.js | ||
| │ └── local_steps.js | ||
| └── support/ | ||
| └── hooks.js # launches Playwright per scenario; SDK routes the launch | ||
| ``` | ||
|
|
||
| ## Notes | ||
|
|
||
| * View your test results on the [BrowserStack Automate dashboard](https://www.browserstack.com/automate). | ||
| * To test on a different set of browsers, see our [list of supported browsers and platforms](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate). | ||
| * Understand how many parallel sessions you need with the [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github). | ||
|
|
||
| ## Further Reading | ||
|
|
||
| - [Cucumber-JS](https://github.com/cucumber/cucumber-js) | ||
| - [Playwright](https://playwright.dev/) | ||
| - [BrowserStack documentation for Playwright](https://www.browserstack.com/docs/automate/playwright) | ||
| - [BrowserStack Node SDK](https://www.npmjs.com/package/browserstack-node-sdk) | ||
|
|
||
| Happy Testing! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # ============================= | ||
| # Set BrowserStack Credentials | ||
| # ============================= | ||
| # Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and | ||
| # BROWSERSTACK_ACCESS_KEY as env variables | ||
| userName: YOUR_USERNAME | ||
| accessKey: YOUR_ACCESS_KEY | ||
|
|
||
| # ====================== | ||
| # BrowserStack Reporting | ||
| # ====================== | ||
| # The following capabilities are used to set up reporting on BrowserStack: | ||
| # Set 'projectName' to the name of your project. Example, Marketing Website | ||
| projectName: BrowserStack Samples | ||
| # Set `buildName` as the name of the job / testsuite being run | ||
| buildName: cucumber-js-playwright-browserstack | ||
| # `buildIdentifier` is a unique id to differentiate every execution that gets appended to | ||
| # buildName. Choose your buildIdentifier format from the available expressions: | ||
| # ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution | ||
| # ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 | ||
| # Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests | ||
| buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} | ||
|
|
||
| # ======================================= | ||
| # Platforms (Browsers / Devices to test) | ||
| # ======================================= | ||
| # Platforms object contains all the browser / device combinations you want to test on. | ||
| # Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) | ||
| # Your step definitions call `chromium.launch()` unconditionally (see features/support/hooks.js) | ||
| # -- the SDK transparently routes the launch to the per-platform browser configured here. | ||
| platforms: | ||
| - os: Windows | ||
| osVersion: 11 | ||
| browserName: chrome | ||
| browserVersion: latest | ||
| - os: OS X | ||
| osVersion: Ventura | ||
| browserName: playwright-webkit | ||
| browserVersion: latest | ||
| - os: Windows | ||
| osVersion: 11 | ||
| browserName: playwright-firefox | ||
| browserVersion: latest | ||
|
|
||
| # ======================= | ||
| # Parallels per Platform | ||
| # ======================= | ||
| # The number of parallel threads to be used for each platform set. | ||
| # BrowserStack's SDK runner will select the best strategy based on the configured value | ||
| # | ||
| # Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack | ||
| # | ||
| # Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack | ||
| parallelsPerPlatform: 1 | ||
|
|
||
| # ========================================== | ||
| # BrowserStack Local | ||
| # (For localhost, staging/private websites) | ||
| # ========================================== | ||
| # Set browserstackLocal to true if your website under test is not accessible publicly over the internet | ||
| # Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction | ||
| browserstackLocal: false # <boolean> (Default false). Set to true to run the local sample test. | ||
| # browserStackLocalOptions: | ||
| # Options to be passed to BrowserStack local in-case of advanced configurations | ||
| # localIdentifier: # <string> (Default: null) Needed if you need to run multiple instances of local. | ||
| # forceLocal: true # <boolean> (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. | ||
| # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections | ||
|
|
||
| framework: playwright | ||
| source: cucumber-js-playwright-browserstack:sample-main:v1.0 | ||
|
|
||
| # =================== | ||
| # Debugging features | ||
| # =================== | ||
| debug: false # <boolean> # Set to true if you need screenshots for every selenium command ran | ||
| networkLogs: false # <boolean> Set to true to enable HAR logs capturing | ||
| consoleLogs: errors # <string> Remote browser's console debug levels to be printed (Default: errors) | ||
| # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) | ||
|
|
||
| # Test Reporting and Analytics is an intelligent test reporting & debugging product. It collects data using the SDK. | ||
| # Visit automation.browserstack.com to see your test reports and insights. To disable, set `testReporting: false`. | ||
| testReporting: true | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Cucumber-JS profiles. The BrowserStack SDK (invoked via `browserstack-node-sdk | ||
| // cucumber-js`) reads browserstack.yml, fans each profile out across the platforms | ||
| // declared there, and routes the Playwright `chromium.launch()` in support/hooks.js | ||
| // to BrowserStack. | ||
| // | ||
| // npm run sample-test -> --profile sample (public bstackdemo) | ||
| // npm run sample-local-test -> --profile local (BrowserStack Local) | ||
| const common = ['features/support/hooks.js']; | ||
|
|
||
| module.exports = { | ||
| sample: { | ||
| paths: ['features/sample.feature'], | ||
| require: ['features/step_definitions/sample_steps.js', ...common], | ||
| }, | ||
| local: { | ||
| paths: ['features/local.feature'], | ||
| require: ['features/step_definitions/local_steps.js', ...common], | ||
| }, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| Feature: BStackLocalSample | ||
|
|
||
| As a developer testing a private host | ||
| I want BrowserStack Local to tunnel my localhost to BrowserStack | ||
| So that the cloud browser can reach a page only my machine can serve | ||
|
|
||
| Scenario: Reach a private host via BrowserStack Local | ||
| Given I open the local sample page on bs-local | ||
| Then the local sample page title contains "BrowserStack Local" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| Feature: BStackDemo cart | ||
|
|
||
| As a shopper on bstackdemo.com | ||
| I want to add an item to my cart | ||
| So that I can verify the cart shows what I picked | ||
|
|
||
| Scenario: Add the first item to cart | ||
| Given I open the bstackdemo home page | ||
| When I add the first product to the cart | ||
| Then the cart shows 1 item that matches the product I added |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| const { Given, Then } = require('@cucumber/cucumber'); | ||
| const assert = require('assert'); | ||
|
|
||
| // With `browserstackLocal: true` in browserstack.yml the SDK opens a tunnel, so the | ||
| // cloud browser can reach http://bs-local.com:<port>/ -- a host only your machine serves. | ||
| Given('I open the local sample page on bs-local', async function () { | ||
| await this.page.goto('http://bs-local.com:45454/'); | ||
| }); | ||
|
|
||
| Then('the local sample page title contains {string}', async function (expected) { | ||
| const title = await this.page.title(); | ||
| assert.ok(title.includes(expected), `expected title to contain "${expected}" but was "${title}"`); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| const { Given, When, Then } = require('@cucumber/cucumber'); | ||
| const assert = require('assert'); | ||
|
|
||
| let productTitle; | ||
|
|
||
| Given('I open the bstackdemo home page', async function () { | ||
| await this.page.goto('https://bstackdemo.com/'); | ||
| }); | ||
|
|
||
| When('I add the first product to the cart', async function () { | ||
| const firstProduct = this.page.locator('[id="1"]'); | ||
| productTitle = await firstProduct.locator('.shelf-item__title').first().innerText(); | ||
| await firstProduct.getByText('Add to Cart').click(); | ||
| }); | ||
|
|
||
| Then('the cart shows 1 item that matches the product I added', async function () { | ||
| const quantity = await this.page.locator('.bag__quantity').innerText(); | ||
| assert.strictEqual(quantity, '1'); | ||
|
|
||
| const cartTitle = await this.page.locator('.shelf-item__details .title').innerText(); | ||
| assert.strictEqual(cartTitle, productTitle); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| const { Before, After, BeforeAll, AfterAll, setDefaultTimeout } = require('@cucumber/cucumber'); | ||
| const { chromium } = require('@playwright/test'); | ||
|
|
||
| // Cucumber steps can take longer than the default 5s on a remote browser. | ||
| setDefaultTimeout(60 * 1000); | ||
|
|
||
| let browser; | ||
|
|
||
| // Your code calls `chromium.launch()` as usual -- the BrowserStack SDK transparently | ||
| // routes the launch to the per-platform browser configured in browserstack.yml. | ||
| BeforeAll(async () => { | ||
| browser = await chromium.launch(); | ||
| }); | ||
|
|
||
| AfterAll(async () => { | ||
| if (browser) await browser.close(); | ||
| }); | ||
|
|
||
| // A fresh context + page per scenario; `this` is the Cucumber World shared with steps. | ||
| Before(async function () { | ||
| this.context = await browser.newContext(); | ||
| this.page = await this.context.newPage(); | ||
| }); | ||
|
|
||
| After(async function () { | ||
| if (this.page) await this.page.close(); | ||
| if (this.context) await this.context.close(); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| { | ||
| "name": "cucumber-js-playwright-browserstack", | ||
| "version": "1.0.0", | ||
| "description": "Sample showing how to run Cucumber-JS + Playwright tests on BrowserStack using the BrowserStack Node SDK", | ||
| "main": "index.js", | ||
| "scripts": { | ||
| "sample-test": "browserstack-node-sdk cucumber-js --profile sample", | ||
| "sample-local-test": "browserstack-node-sdk cucumber-js --profile local", | ||
| "postinstall": "npm update browserstack-node-sdk" | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "devDependencies": { | ||
| "@cucumber/cucumber": "^11.0.0", | ||
| "@playwright/test": "latest", | ||
| "browserstack-node-sdk": "latest" | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.