From a68777f8fae006ac1d8adcdf4509c23b697e9b67 Mon Sep 17 00:00:00 2001
From: shreybaz <shreyansh@gobazzinga.io>
Date: Wed, 24 Apr 2024 17:51:04 +0530
Subject: [PATCH] added lighthouse CI test and playwrighgt test

---
 .github/workflows/playwright.yml     |  47 +++
 .gitignore                           |   6 +
 README.md                            |   0
 lighthouserc.js                      |  16 +
 package-lock.json                    |  87 ++++++
 package.json                         |  10 +
 playwright.config.js                 |  79 +++++
 tests-examples/demo-todo-app.spec.js | 449 +++++++++++++++++++++++++++
 tests/example.spec.js                | 180 +++++++++++
 9 files changed, 874 insertions(+)
 create mode 100644 .github/workflows/playwright.yml
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 lighthouserc.js
 create mode 100644 package-lock.json
 create mode 100644 package.json
 create mode 100644 playwright.config.js
 create mode 100644 tests-examples/demo-todo-app.spec.js
 create mode 100644 tests/example.spec.js

diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
new file mode 100644
index 0000000..626736c
--- /dev/null
+++ b/.github/workflows/playwright.yml
@@ -0,0 +1,47 @@
+name: Generate lighthouse test report on PR
+on:
+    push: 
+      branches: [ main ]
+    pull_request:
+        branches: [ main ]
+        
+jobs:
+    automation-test:
+        runs-on: ubuntu-latest
+        env: 
+            COOKIE_KEY: '1267b291500365c42043e04bc69cf24a31495bd8936fc8d6794283675e288fad755971922d45cf1ca0b438df4fc847f39cb0b2aceb3a45673eff231cddb88dc9'
+
+        steps:
+          - name: Checkout repo
+            uses: actions/checkout@v3
+
+          - name: Install Node.js
+            uses: actions/setup-node@v4
+            with: 
+                node-version: 20
+          - run: npm install
+
+          - name: Install Lighthouse
+            run: npm install -g @lhci/cli@0.13.x
+
+          - name: Run Lighthouse
+            # run: lhci autorun --upload.githubToken="$LHCI_GITHUB_TOKEN" || echo "LHCI failed!"
+            run: lhci autorun 
+            # env: 
+            #     LHCI_GITHUB_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
+
+          - name: install playwright
+            run: npx playwright install    
+
+          - name: run playwright
+            id: tests
+            # run: TESTPARAM=${{secrets.MAIL_PASSWORD}} npx playwright test --project=firefox
+            run: npx playwright test --project=firefox
+
+          # - name: Check test result and comment
+          #   uses: peter-evans/create-or-update-comment@v1
+          #   with:
+          #     token: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
+          #     issue-number: ${{ github.event.pull_request.number }}
+          #     body: |
+          #       ${{ steps.tests.outcome == '2 passed' && 'Test passed :heavy_check_mark:' || 'Test failed, Please check logs for details.' }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..203df33
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+node_modules/
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
+/.lighthouseci
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/lighthouserc.js b/lighthouserc.js
new file mode 100644
index 0000000..2082feb
--- /dev/null
+++ b/lighthouserc.js
@@ -0,0 +1,16 @@
+module.exports = {
+    ci: {
+        collect: {
+        url: ['https://yral.com/'],
+        },
+        upload: {
+            target: 'temporary-public-storage',
+        },
+        assert: {
+            assertions: {
+                "categories:performance": ["warn", {"minScore": 0.3}],
+                "categories:accessibility": ["warn", {"minScore": 0.5}]
+            },
+        },
+    },
+};
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..b0a8dfa
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,87 @@
+{
+  "name": "hot-or-not-web-leptos-ssr-e2e-test",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "dependencies": {
+        "playwright": "^1.43.1"
+      },
+      "devDependencies": {
+        "@playwright/test": "^1.43.1",
+        "@types/node": "^20.12.7"
+      }
+    },
+    "node_modules/@playwright/test": {
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz",
+      "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==",
+      "dev": true,
+      "dependencies": {
+        "playwright": "1.43.1"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "20.12.7",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
+      "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
+      "dev": true,
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/playwright": {
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz",
+      "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==",
+      "dependencies": {
+        "playwright-core": "1.43.1"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "optionalDependencies": {
+        "fsevents": "2.3.2"
+      }
+    },
+    "node_modules/playwright-core": {
+      "version": "1.43.1",
+      "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz",
+      "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==",
+      "bin": {
+        "playwright-core": "cli.js"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "dev": true
+    }
+  }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..819643c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,10 @@
+{
+  "dependencies": {
+    "playwright": "^1.43.1"
+  },
+  "devDependencies": {
+    "@playwright/test": "^1.43.1",
+    "@types/node": "^20.12.7"
+  },
+  "scripts": {}
+}
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 0000000..b15af2e
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,79 @@
+// @ts-check
+const { defineConfig, devices } = require('@playwright/test');
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * @see https://playwright.dev/docs/test-configuration
+ */
+module.exports = defineConfig({
+  testDir: './tests',
+  /* Run tests in files in parallel */
+  fullyParallel: true,
+  /* Fail the build on CI if you accidentally left test.only in the source code. */
+  forbidOnly: !!process.env.CI,
+  /* Retry on CI only */
+  retries: process.env.CI ? 2 : 0,
+  /* Opt out of parallel tests on CI. */
+  workers: process.env.CI ? 1 : undefined,
+  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+  reporter: 'html',
+  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+  use: {
+    /* Base URL to use in actions like `await page.goto('/')`. */
+    // baseURL: 'http://127.0.0.1:3000',
+
+    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+    trace: 'on-first-retry',
+  },
+
+  /* Configure projects for major browsers */
+  projects: [
+    {
+      name: 'chromium',
+      use: { ...devices['Desktop Chrome'] },
+    },
+
+    {
+      name: 'firefox',
+      use: { ...devices['Desktop Firefox'] },
+    },
+
+    {
+      name: 'webkit',
+      use: { ...devices['Desktop Safari'] },
+    },
+
+    /* Test against mobile viewports. */
+    // {
+    //   name: 'Mobile Chrome',
+    //   use: { ...devices['Pixel 5'] },
+    // },
+    // {
+    //   name: 'Mobile Safari',
+    //   use: { ...devices['iPhone 12'] },
+    // },
+
+    /* Test against branded browsers. */
+    // {
+    //   name: 'Microsoft Edge',
+    //   use: { ...devices['Desktop Edge'], channel: 'msedge' },
+    // },
+    // {
+    //   name: 'Google Chrome',
+    //   use: { ...devices['Desktop Chrome'], channel: 'chrome' },
+    // },
+  ],
+
+  /* Run your local dev server before starting the tests */
+  // webServer: {
+  //   command: 'npm run start',
+  //   url: 'http://127.0.0.1:3000',
+  //   reuseExistingServer: !process.env.CI,
+  // },
+});
+
diff --git a/tests-examples/demo-todo-app.spec.js b/tests-examples/demo-todo-app.spec.js
new file mode 100644
index 0000000..e2eb87c
--- /dev/null
+++ b/tests-examples/demo-todo-app.spec.js
@@ -0,0 +1,449 @@
+// @ts-check
+const { test, expect } = require('@playwright/test');
+
+test.beforeEach(async ({ page }) => {
+  await page.goto('https://demo.playwright.dev/todomvc');
+});
+
+const TODO_ITEMS = [
+  'buy some cheese',
+  'feed the cat',
+  'book a doctors appointment'
+];
+
+test.describe('New Todo', () => {
+  test('should allow me to add todo items', async ({ page }) => {
+    // create a new todo locator
+    const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    // Create 1st todo.
+    await newTodo.fill(TODO_ITEMS[0]);
+    await newTodo.press('Enter');
+
+    // Make sure the list only has one todo item.
+    await expect(page.getByTestId('todo-title')).toHaveText([
+      TODO_ITEMS[0]
+    ]);
+
+    // Create 2nd todo.
+    await newTodo.fill(TODO_ITEMS[1]);
+    await newTodo.press('Enter');
+
+    // Make sure the list now has two todo items.
+    await expect(page.getByTestId('todo-title')).toHaveText([
+      TODO_ITEMS[0],
+      TODO_ITEMS[1]
+    ]);
+
+    await checkNumberOfTodosInLocalStorage(page, 2);
+  });
+
+  test('should clear text input field when an item is added', async ({ page }) => {
+    // create a new todo locator
+    const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    // Create one todo item.
+    await newTodo.fill(TODO_ITEMS[0]);
+    await newTodo.press('Enter');
+
+    // Check that input is empty.
+    await expect(newTodo).toBeEmpty();
+    await checkNumberOfTodosInLocalStorage(page, 1);
+  });
+
+  test('should append new items to the bottom of the list', async ({ page }) => {
+    // Create 3 items.
+    await createDefaultTodos(page);
+
+    // create a todo count locator
+    const todoCount = page.getByTestId('todo-count')
+  
+    // Check test using different methods.
+    await expect(page.getByText('3 items left')).toBeVisible();
+    await expect(todoCount).toHaveText('3 items left');
+    await expect(todoCount).toContainText('3');
+    await expect(todoCount).toHaveText(/3/);
+
+    // Check all items in one call.
+    await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
+    await checkNumberOfTodosInLocalStorage(page, 3);
+  });
+});
+
+test.describe('Mark all as completed', () => {
+  test.beforeEach(async ({ page }) => {
+    await createDefaultTodos(page);
+    await checkNumberOfTodosInLocalStorage(page, 3);
+  });
+
+  test.afterEach(async ({ page }) => {
+    await checkNumberOfTodosInLocalStorage(page, 3);
+  });
+
+  test('should allow me to mark all items as completed', async ({ page }) => {
+    // Complete all todos.
+    await page.getByLabel('Mark all as complete').check();
+
+    // Ensure all todos have 'completed' class.
+    await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
+    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+  });
+
+  test('should allow me to clear the complete state of all items', async ({ page }) => {
+    const toggleAll = page.getByLabel('Mark all as complete');
+    // Check and then immediately uncheck.
+    await toggleAll.check();
+    await toggleAll.uncheck();
+
+    // Should be no completed classes.
+    await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
+  });
+
+  test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
+    const toggleAll = page.getByLabel('Mark all as complete');
+    await toggleAll.check();
+    await expect(toggleAll).toBeChecked();
+    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+    // Uncheck first todo.
+    const firstTodo = page.getByTestId('todo-item').nth(0);
+    await firstTodo.getByRole('checkbox').uncheck();
+
+    // Reuse toggleAll locator and make sure its not checked.
+    await expect(toggleAll).not.toBeChecked();
+
+    await firstTodo.getByRole('checkbox').check();
+    await checkNumberOfCompletedTodosInLocalStorage(page, 3);
+
+    // Assert the toggle all is checked again.
+    await expect(toggleAll).toBeChecked();
+  });
+});
+
+test.describe('Item', () => {
+
+  test('should allow me to mark items as complete', async ({ page }) => {
+    // create a new todo locator
+    const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    // Create two items.
+    for (const item of TODO_ITEMS.slice(0, 2)) {
+      await newTodo.fill(item);
+      await newTodo.press('Enter');
+    }
+
+    // Check first item.
+    const firstTodo = page.getByTestId('todo-item').nth(0);
+    await firstTodo.getByRole('checkbox').check();
+    await expect(firstTodo).toHaveClass('completed');
+
+    // Check second item.
+    const secondTodo = page.getByTestId('todo-item').nth(1);
+    await expect(secondTodo).not.toHaveClass('completed');
+    await secondTodo.getByRole('checkbox').check();
+
+    // Assert completed class.
+    await expect(firstTodo).toHaveClass('completed');
+    await expect(secondTodo).toHaveClass('completed');
+  });
+
+  test('should allow me to un-mark items as complete', async ({ page }) => {
+     // create a new todo locator
+     const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    // Create two items.
+    for (const item of TODO_ITEMS.slice(0, 2)) {
+      await newTodo.fill(item);
+      await newTodo.press('Enter');
+    }
+
+    const firstTodo = page.getByTestId('todo-item').nth(0);
+    const secondTodo = page.getByTestId('todo-item').nth(1);
+    const firstTodoCheckbox = firstTodo.getByRole('checkbox');
+
+    await firstTodoCheckbox.check();
+    await expect(firstTodo).toHaveClass('completed');
+    await expect(secondTodo).not.toHaveClass('completed');
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+    await firstTodoCheckbox.uncheck();
+    await expect(firstTodo).not.toHaveClass('completed');
+    await expect(secondTodo).not.toHaveClass('completed');
+    await checkNumberOfCompletedTodosInLocalStorage(page, 0);
+  });
+
+  test('should allow me to edit an item', async ({ page }) => {
+    await createDefaultTodos(page);
+
+    const todoItems = page.getByTestId('todo-item');
+    const secondTodo = todoItems.nth(1);
+    await secondTodo.dblclick();
+    await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
+    await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+    await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+    // Explicitly assert the new text value.
+    await expect(todoItems).toHaveText([
+      TODO_ITEMS[0],
+      'buy some sausages',
+      TODO_ITEMS[2]
+    ]);
+    await checkTodosInLocalStorage(page, 'buy some sausages');
+  });
+});
+
+test.describe('Editing', () => {
+  test.beforeEach(async ({ page }) => {
+    await createDefaultTodos(page);
+    await checkNumberOfTodosInLocalStorage(page, 3);
+  });
+
+  test('should hide other controls when editing', async ({ page }) => {
+    const todoItem = page.getByTestId('todo-item').nth(1);
+    await todoItem.dblclick();
+    await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
+    await expect(todoItem.locator('label', {
+      hasText: TODO_ITEMS[1],
+    })).not.toBeVisible();
+    await checkNumberOfTodosInLocalStorage(page, 3);
+  });
+
+  test('should save edits on blur', async ({ page }) => {
+    const todoItems = page.getByTestId('todo-item');
+    await todoItems.nth(1).dblclick();
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
+
+    await expect(todoItems).toHaveText([
+      TODO_ITEMS[0],
+      'buy some sausages',
+      TODO_ITEMS[2],
+    ]);
+    await checkTodosInLocalStorage(page, 'buy some sausages');
+  });
+
+  test('should trim entered text', async ({ page }) => {
+    const todoItems = page.getByTestId('todo-item');
+    await todoItems.nth(1).dblclick();
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('    buy some sausages    ');
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+    await expect(todoItems).toHaveText([
+      TODO_ITEMS[0],
+      'buy some sausages',
+      TODO_ITEMS[2],
+    ]);
+    await checkTodosInLocalStorage(page, 'buy some sausages');
+  });
+
+  test('should remove the item if an empty text string was entered', async ({ page }) => {
+    const todoItems = page.getByTestId('todo-item');
+    await todoItems.nth(1).dblclick();
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
+
+    await expect(todoItems).toHaveText([
+      TODO_ITEMS[0],
+      TODO_ITEMS[2],
+    ]);
+  });
+
+  test('should cancel edits on escape', async ({ page }) => {
+    const todoItems = page.getByTestId('todo-item');
+    await todoItems.nth(1).dblclick();
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
+    await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
+    await expect(todoItems).toHaveText(TODO_ITEMS);
+  });
+});
+
+test.describe('Counter', () => {
+  test('should display the current number of todo items', async ({ page }) => {
+    // create a new todo locator
+    const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    // create a todo count locator
+    const todoCount = page.getByTestId('todo-count')
+
+    await newTodo.fill(TODO_ITEMS[0]);
+    await newTodo.press('Enter');
+    await expect(todoCount).toContainText('1');
+
+    await newTodo.fill(TODO_ITEMS[1]);
+    await newTodo.press('Enter');
+    await expect(todoCount).toContainText('2');
+
+    await checkNumberOfTodosInLocalStorage(page, 2);
+  });
+});
+
+test.describe('Clear completed button', () => {
+  test.beforeEach(async ({ page }) => {
+    await createDefaultTodos(page);
+  });
+
+  test('should display the correct text', async ({ page }) => {
+    await page.locator('.todo-list li .toggle').first().check();
+    await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
+  });
+
+  test('should remove completed items when clicked', async ({ page }) => {
+    const todoItems = page.getByTestId('todo-item');
+    await todoItems.nth(1).getByRole('checkbox').check();
+    await page.getByRole('button', { name: 'Clear completed' }).click();
+    await expect(todoItems).toHaveCount(2);
+    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+  });
+
+  test('should be hidden when there are no items that are completed', async ({ page }) => {
+    await page.locator('.todo-list li .toggle').first().check();
+    await page.getByRole('button', { name: 'Clear completed' }).click();
+    await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
+  });
+});
+
+test.describe('Persistence', () => {
+  test('should persist its data', async ({ page }) => {
+    // create a new todo locator
+    const newTodo = page.getByPlaceholder('What needs to be done?');
+
+    for (const item of TODO_ITEMS.slice(0, 2)) {
+      await newTodo.fill(item);
+      await newTodo.press('Enter');
+    }
+
+    const todoItems = page.getByTestId('todo-item');
+    const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
+    await firstTodoCheck.check();
+    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+    await expect(firstTodoCheck).toBeChecked();
+    await expect(todoItems).toHaveClass(['completed', '']);
+
+    // Ensure there is 1 completed item.
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+    // Now reload.
+    await page.reload();
+    await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
+    await expect(firstTodoCheck).toBeChecked();
+    await expect(todoItems).toHaveClass(['completed', '']);
+  });
+});
+
+test.describe('Routing', () => {
+  test.beforeEach(async ({ page }) => {
+    await createDefaultTodos(page);
+    // make sure the app had a chance to save updated todos in storage
+    // before navigating to a new view, otherwise the items can get lost :(
+    // in some frameworks like Durandal
+    await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
+  });
+
+  test('should allow me to display active items', async ({ page }) => {
+    const todoItem = page.getByTestId('todo-item');
+    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+    
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+    await page.getByRole('link', { name: 'Active' }).click();
+    await expect(todoItem).toHaveCount(2);
+    await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
+  });
+
+  test('should respect the back button', async ({ page }) => {
+    const todoItem = page.getByTestId('todo-item');
+    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+
+    await test.step('Showing all items', async () => {
+      await page.getByRole('link', { name: 'All' }).click();
+      await expect(todoItem).toHaveCount(3);
+    });
+
+    await test.step('Showing active items', async () => {
+      await page.getByRole('link', { name: 'Active' }).click();
+    });
+
+    await test.step('Showing completed items', async () => {
+      await page.getByRole('link', { name: 'Completed' }).click();
+    });
+
+    await expect(todoItem).toHaveCount(1);
+    await page.goBack();
+    await expect(todoItem).toHaveCount(2);
+    await page.goBack();
+    await expect(todoItem).toHaveCount(3);
+  });
+
+  test('should allow me to display completed items', async ({ page }) => {
+    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+    await page.getByRole('link', { name: 'Completed' }).click();
+    await expect(page.getByTestId('todo-item')).toHaveCount(1);
+  });
+
+  test('should allow me to display all items', async ({ page }) => {
+    await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
+    await checkNumberOfCompletedTodosInLocalStorage(page, 1);
+    await page.getByRole('link', { name: 'Active' }).click();
+    await page.getByRole('link', { name: 'Completed' }).click();
+    await page.getByRole('link', { name: 'All' }).click();
+    await expect(page.getByTestId('todo-item')).toHaveCount(3);
+  });
+
+  test('should highlight the currently applied filter', async ({ page }) => {
+    await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
+
+    //create locators for active and completed links
+    const activeLink = page.getByRole('link', { name: 'Active' });
+    const completedLink = page.getByRole('link', { name: 'Completed' });
+    await activeLink.click();
+
+    // Page change - active items.
+    await expect(activeLink).toHaveClass('selected');
+    await completedLink.click();
+
+    // Page change - completed items.
+    await expect(completedLink).toHaveClass('selected');
+  });
+});
+
+async function createDefaultTodos(page) {
+  // create a new todo locator
+  const newTodo = page.getByPlaceholder('What needs to be done?');
+
+  for (const item of TODO_ITEMS) {
+    await newTodo.fill(item);
+    await newTodo.press('Enter');
+  }
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ * @param {number} expected
+ */
+ async function checkNumberOfTodosInLocalStorage(page, expected) {
+  return await page.waitForFunction(e => {
+    return JSON.parse(localStorage['react-todos']).length === e;
+  }, expected);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ * @param {number} expected
+ */
+ async function checkNumberOfCompletedTodosInLocalStorage(page, expected) {
+  return await page.waitForFunction(e => {
+    return JSON.parse(localStorage['react-todos']).filter(i => i.completed).length === e;
+  }, expected);
+}
+
+/**
+ * @param {import('@playwright/test').Page} page
+ * @param {string} title
+ */
+async function checkTodosInLocalStorage(page, title) {
+  return await page.waitForFunction(t => {
+    return JSON.parse(localStorage['react-todos']).map(i => i.title).includes(t);
+  }, title);
+}
diff --git a/tests/example.spec.js b/tests/example.spec.js
new file mode 100644
index 0000000..cd4f49a
--- /dev/null
+++ b/tests/example.spec.js
@@ -0,0 +1,180 @@
+import { test, expect } from '@playwright/test';
+
+test.beforeEach(async ({ page }) => {
+  await page.goto('https://yral.com/');
+});
+
+test.describe('New test', () => {
+  test('Has title', async ({page}) => {
+
+    try {    // Check opening status
+      const title = await page.title();
+      console.log(title); // Log the page title
+      // expect(title).toContain('Expected Title'); // Replace with expected title
+      expect(title).toBe("Yral");
+    } catch (error) {
+          console.error("error checking title",error); // Handle errors gracefully
+    }
+    
+  });
+
+  test('Test video playing', async ({page}) => {
+
+    //try with 'video' element
+    let video = page.locator('video').nth(0);
+    await page.waitForTimeout(3000);
+
+    await expect(video).toBeVisible();
+    console.log("is visible");
+
+    await expect(video).not.toHaveAttribute('paused');
+    console.log("is not paused");
+
+    await expect(video).toHaveAttribute('muted'); 
+    console.log("is muted");
+
+    // click unmute button
+    await page.locator('.fixed').first().click();
+    await page.waitForTimeout(3000);
+    //  // css selector of unmute button
+    // const buttonLocator = page.locator('body > main > div.h-full.w-full.overflow-hidden.overflow-y-auto > div > button > svg > path').click();
+
+    let video_new = page.locator('video').nth(0);
+    // await expect(video_new).not.toHaveAttribute('muted'); 
+    // console.log("is not muted");
+
+    const duration = await video.evaluate(videoEle => videoEle.duration);
+    console.log('Video Duration:', duration);
+
+    // const isVideoPlaying = await video.evaluate(() => document.querySelector('video').autoplay); // Check playback state
+    // console.log("is video playing", isVideoPlaying);
+
+    //check if video is playing after 5 seconds
+    await page.waitForTimeout(5000);
+    try {  
+        await expect(video).not.toHaveAttribute('paused');
+        console.log("after 5 seconds, not paused");
+    } catch (error) {
+      console.error("video paused after 5 seconds",error); // Handle errors gracefully
+    }
+
+    //scroll to new video based on its locator
+    let new_video = page.locator('video').nth(2);
+    await new_video.scrollIntoViewIfNeeded();
+    console.log("scroll to new video");
+
+    await expect(new_video).toBeVisible();
+    console.log("2nd video is visible");
+
+    await expect(new_video).not.toHaveAttribute('paused');
+    console.log("2nd video is not paused");
+
+    const new_duration = await new_video.evaluate(new_video => new_video.duration);
+    console.log('2nd Video Duration:', new_duration);
+
+    await page.waitForTimeout(3000);
+
+    //scroll to new video based on its locator
+    let third_video = page.locator('video').nth(5);
+    await third_video.scrollIntoViewIfNeeded();
+    console.log("scroll to third video");
+
+    await expect(third_video).toBeVisible();
+    console.log("3rd video is visible");
+
+    await expect(third_video).not.toHaveAttribute('paused');
+    console.log("3rd video is not paused");
+    
+    const third_duration = await third_video.evaluate(new_video => new_video.duration);
+    console.log('3rd Video Duration:', third_duration);
+
+    // await page.waitForTimeout(3000);
+
+  });
+
+  // test('Test google login', async ({page}) => {
+
+  //   // login from wallet: using headfull mode
+  //   try {
+  //     console.log('Log in test');
+      
+  //     // const environment_pass = global.expect;
+  //     const environment_pass = process.env.TESTPARAM; 
+
+  //     await page.waitForTimeout(3000);
+
+  //     await page.getByRole('navigation').getByRole('link').nth(3).click();
+  //     await page.getByRole('button', { name: 'Login to claim your COYNs' }).click();
+  //     const page1Promise = page.waitForEvent('popup');
+  //     await page.getByRole('button', { name: 'Google Sign-In' }).click();
+  //     const page1 = await page1Promise;
+  //     await page1.getByLabel('Email or phone').click();
+  //     await page1.getByLabel('Email or phone').fill('testautomationyral@gmail.com');
+      
+  //     await page.waitForTimeout(2000);
+  //     await page1.getByLabel('Email or phone').press('Enter');
+      
+  //     await page1.getByLabel('Enter your password').click();
+  //     await page1.getByLabel('Enter your password').fill(environment_pass);
+  //     await page.waitForTimeout(2000);
+
+  //     await page1.getByLabel('Enter your password').press('Enter');
+  //     // await page1.pause();
+  //     await page.waitForTimeout(3000);
+
+  //     console.log('Logged in succesfully');
+  //     // await page.getByRole('navigation').getByRole('link').first().click();
+
+  //     await page.waitForTimeout(3000);
+  //       //request to the API endpoint to fetch the response
+  //     const response = await fetch('https://yral-metadata.fly.dev/metadata/gzlng-jqzta-5kubz-4nyam-5so2e-tsoio-ijv2s-47dsw-7ksd7-pe3eb-zqe');
+  //     await page.waitForTimeout(2000);
+
+  //     console.log(response.status);
+  //     const responseData = await response.json();
+  //     console.log(responseData);
+
+  //   } catch (error) {
+  //     console.error("error in login"); 
+  //   }
+  // });
+
+  // test('TEST UPLOAD FLOW', async ({page}) => {
+
+  //   console.log('Uploading test');
+
+  //   await page.waitForTimeout(2000);
+
+  //   await page.getByRole('navigation').getByRole('link').nth(2).click();
+
+  //   await page.setInputFiles('#dropzone-file', './test1.mp4');
+
+  //   // TODO
+  //   // //endpoint to fetch the response
+  //   // const response = await fetch('https://yral.com/api/stream_to_offchain_agent11958048345285885967');
+  //   // console.log(response);
+  //   // const responseData = await response.json();
+
+  //   // console.log(responseData);
+  //   // // Perform assertions based on the API response
+  //   // expect(responseData.status).toBe(200);
+
+  //   await page.getByPlaceholder('Write your description here..').click();
+  //   await page.getByPlaceholder('Write your description here..').fill('test video upload');
+  //   await page.getByPlaceholder('#hashtag1,#hashtag2,#hashtag3').click();
+  //   await page.getByPlaceholder('#hashtag1,#hashtag2,#hashtag3').fill('#try');
+
+  //   // const button = 
+  //   // await button.click('button[data-api-trigger="true"]');
+
+  //   await page.getByRole('button', { name: 'Upload Video' }).click();
+  //   await page.pause();
+
+  //   // await button.click('button[data-api-trigger="true"]');
+
+  //   await page.getByRole('navigation').getByRole('link').first().click();
+  //   await page.waitForTimeout(5000);
+
+  // });
+
+});