Skip to content

Commit

Permalink
Merge pull request #306 from QAComet/qacomet/search-tests
Browse files Browse the repository at this point in the history
Added tests for search
  • Loading branch information
VariableVic authored Apr 16, 2024
2 parents 1df8fc5 + 98b8ece commit 1810b9a
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 9 deletions.
117 changes: 117 additions & 0 deletions .github/scripts/medusa-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const dotenv = require("dotenv");

let ENV_FILE_NAME = "";
switch (process.env.NODE_ENV) {
case "production":
ENV_FILE_NAME = ".env.production";
break;
case "staging":
ENV_FILE_NAME = ".env.staging";
break;
case "test":
ENV_FILE_NAME = ".env.test";
break;
case "development":
default:
ENV_FILE_NAME = ".env";
break;
}

try {
dotenv.config({ path: process.cwd() + "/" + ENV_FILE_NAME });
} catch (e) {}

// CORS when consuming Medusa from admin
const ADMIN_CORS =
process.env.ADMIN_CORS || "http://localhost:7000,http://localhost:7001";

// CORS to avoid issues when consuming Medusa from a client
const STORE_CORS = process.env.STORE_CORS || "http://localhost:8000";

const DATABASE_URL =
process.env.DATABASE_URL || "postgres://medusa:password@localhost/medusa";

const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";

const plugins = [
`medusa-fulfillment-manual`,
`medusa-payment-manual`,
{
resolve: `@medusajs/file-local`,
options: {
upload_dir: "uploads",
},
},
{
resolve: "@medusajs/admin",
/** @type {import('@medusajs/admin').PluginOptions} */
options: {
autoRebuild: true,
develop: {
open: process.env.OPEN_BROWSER !== "false",
},
},
},
{
resolve: `medusa-plugin-meilisearch`,
options: {
config: {
host: process.env.MEILISEARCH_HOST,
apiKey: process.env.MEILISEARCH_API_KEY,
},
settings: {
products: {
indexSettings: {
searchableAttributes: [
"title",
"description",
"variant_sku",
],
displayedAttributes: [
"id",
"title",
"description",
"variant_sku",
"thumbnail",
"handle",
],
},
primaryKey: "id",
},
},
},
},
];

const modules = {
/*eventBus: {
resolve: "@medusajs/event-bus-redis",
options: {
redisUrl: REDIS_URL
}
},
cacheService: {
resolve: "@medusajs/cache-redis",
options: {
redisUrl: REDIS_URL
}
},*/
};

/** @type {import('@medusajs/medusa').ConfigModule["projectConfig"]} */
const projectConfig = {
jwtSecret: process.env.JWT_SECRET,
cookieSecret: process.env.COOKIE_SECRET,
store_cors: STORE_CORS,
database_url: DATABASE_URL,
admin_cors: ADMIN_CORS,
// Uncomment the following lines to enable REDIS
redis_url: REDIS_URL
};

/** @type {import('@medusajs/medusa').ConfigModule} */
module.exports = {
projectConfig,
plugins,
modules,
};
24 changes: 20 additions & 4 deletions .github/workflows/test-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,16 @@ env:
DATABASE_TYPE: "postgres"
REDIS_URL: redis://localhost:6379
DATABASE_URL: postgres://test_medusa_user:password@localhost/test_medusa_db
MEILISEARCH_HOST: http://localhost:7700
MEILISEARCH_API_KEY: meili_api_key

NEXT_PUBLIC_BASE_URL: http://localhost:8000
NEXT_PUBLIC_DEFAULT_REGION: us
NEXT_PUBLIC_MEDUSA_BACKEND_URL: http://localhost:9000
NEXT_PUBLIC_INDEX_NAME: products
NEXT_PUBLIC_SEARCH_ENDPOINT: http://127.0.0.1:7700
NEXT_PUBLIC_SEARCH_API_KEY: meili_api_key
REVALIDATE_SECRET: supersecret

jobs:
e2e-test-runner:
Expand All @@ -53,6 +62,9 @@ jobs:
meilisearch:
image: getmeili/meilisearch:v1.7
env:
MEILI_MASTER_KEY: meili_api_key
MEILI_ENV: development
ports:
- 7700:7700
options: >-
Expand Down Expand Up @@ -100,11 +112,18 @@ jobs:
--db-database ${{ env.TEST_POSTGRES_DATABASE }} \
--db-host ${{ env.TEST_POSTGRES_HOST }} \
--db-port ${{ env.TEST_POSTGREST_PORT }}
- name: Build the backend
working-directory: ../backend
run: yarn build:admin

- name: Setup search in the backend
working-directory: ../backend
run: yarn add medusa-plugin-meilisearch

- name: Move custom medusa config to the backend
run: cp .github/scripts/medusa-config.js ../backend/medusa-config.js

- name: Seed data from default seed file
working-directory: ../backend
run: medusa seed --seed-file=data/seed.json
Expand All @@ -119,9 +138,6 @@ jobs:
- name: Install playwright
run: yarn playwright install --with-deps

- name: Copy environment
run: cp .env.template .env

- name: Setup frontend
run: yarn build

Expand Down
3 changes: 3 additions & 0 deletions e2e/fixtures/base/base-page.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { CartDropdown } from "./cart-dropdown"
import { NavMenu } from "./nav-menu"
import { Page, Locator } from "@playwright/test"
import { SearchModal } from "./search-modal"

export class BasePage {
page: Page
navMenu: NavMenu
cartDropdown: CartDropdown
searchModal: SearchModal
accountLink: Locator
searchLink: Locator
storeLink: Locator
Expand All @@ -15,6 +17,7 @@ export class BasePage {
this.page = page
this.navMenu = new NavMenu(page)
this.cartDropdown = new CartDropdown(page)
this.searchModal = new SearchModal(page)
this.accountLink = page.getByTestId("nav-account-link")
this.storeLink = page.getByTestId("nav-store-link")
this.searchLink = page.getByTestId("nav-search-link")
Expand Down
36 changes: 36 additions & 0 deletions e2e/fixtures/base/search-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Page, Locator } from "@playwright/test"
import { BaseModal } from "./base-modal"
import { NavMenu } from "./nav-menu"

export class SearchModal extends BaseModal {
searchInput: Locator
searchResults: Locator
noSearchResultsContainer: Locator
searchResult: Locator
searchResultTitle: Locator

constructor(page: Page) {
super(page, page.getByTestId("search-modal-container"))
this.searchInput = this.container.getByTestId("search-input")
this.searchResults = this.container.getByTestId("search-results")
this.noSearchResultsContainer = this.container.getByTestId(
"no-search-results-container"
)
this.searchResult = this.container.getByTestId("search-result")
this.searchResultTitle = this.container.getByTestId("search-result-title")
}

async open() {
const menu = new NavMenu(this.page)
await menu.open()
await menu.searchLink.click()
await this.container.waitFor({ state: "visible" })
}

async close() {
const viewport = this.page.viewportSize()
const y = viewport ? viewport.height / 2 : 100
await this.page.mouse.click(1, y, { clickCount: 2, delay: 100 })
await this.container.waitFor({ state: "hidden" })
}
}
71 changes: 71 additions & 0 deletions e2e/tests/public/search.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { test, expect } from "../../index"

test.describe("Search tests", async () => {
test("Searching for a specific product returns the correct product page", async ({
productPage,
}) => {
const searchModal = productPage.searchModal
await searchModal.open()
await searchModal.searchInput.fill("Sweatshirt")
await searchModal.searchResult
.filter({ hasText: "Sweatshirt" })
.first()
.click()
await productPage.container.waitFor({ state: "visible" })
await expect(productPage.productTitle).toContainText("Sweatshirt")
})

test("An erroneous search returns an empty result", async ({
productPage,
}) => {
const searchModal = productPage.searchModal
await searchModal.open()
await searchModal.searchInput.fill("Does Not Sweatshirt")
await expect(searchModal.noSearchResultsContainer).toBeVisible()
})

test("User can search after an empty search result", async ({
productPage,
}) => {
const searchModal = productPage.searchModal

await searchModal.open()
await searchModal.searchInput.fill("Does Not Sweatshirt")
await expect(searchModal.noSearchResultsContainer).toBeVisible()

await searchModal.searchInput.fill("Sweat")
await expect(searchModal.searchResults).toBeVisible()
await expect(searchModal.searchResult.first()).toBeVisible()
})

test("Closing the search page returns user back to their current page", async ({
storePage,
productPage,
loginPage,
}) => {
const searchModal = storePage.searchModal
await test.step("Navigate to the store page and open and close search modal", async () => {
await storePage.goto()
await searchModal.open()
await searchModal.close()
await expect(storePage.container).toBeVisible()
})

await test.step("Navigate to the product page and open and close search modal", async () => {
await storePage.goto()
const product = await storePage.getProduct("Sweatshirt")
await product.locator.click()
await productPage.container.waitFor({ state: "visible" })
await searchModal.open()
await searchModal.close()
await expect(productPage.container).toBeVisible()
})

await test.step("Navigate to the login page and open and close search modal", async () => {
await loginPage.goto()
await searchModal.open()
await searchModal.close()
await expect(loginPage.container).toBeVisible()
})
})
})
12 changes: 10 additions & 2 deletions src/modules/search/components/hit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ type HitProps = {

const Hit = ({ hit }: HitProps) => {
return (
<LocalizedClientLink href={`/products/${hit.handle}`}>
<LocalizedClientLink
href={`/products/${hit.handle}`}
data-testid="search-result"
>
<Container
key={hit.id}
className="flex sm:flex-col gap-2 w-full p-4 shadow-elevation-card-rest hover:shadow-elevation-card-hover items-center sm:justify-center"
Expand All @@ -33,7 +36,12 @@ const Hit = ({ hit }: HitProps) => {
/>
<div className="flex flex-col justify-between group">
<div className="flex flex-col">
<Text className="text-ui-fg-subtle">{hit.title}</Text>
<Text
className="text-ui-fg-subtle"
data-testid="search-result-title"
>
{hit.title}
</Text>
</div>
</div>
</Container>
Expand Down
5 changes: 4 additions & 1 deletion src/modules/search/components/hits/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ const Hits = ({
}
)}
>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4">
<div
className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4"
data-testid="search-results"
>
{hits.slice(0, 6).map((hit, index) => (
<li
key={index}
Expand Down
1 change: 1 addition & 0 deletions src/modules/search/components/search-box/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const ControlledSearchBox = ({
<div className="flex items-center justify-between">
<input
ref={inputRef}
data-testid="search-input"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
Expand Down
5 changes: 4 additions & 1 deletion src/modules/search/components/show-all/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ const ShowAll = () => {

if (hits.length === 0) {
return (
<Container className="flex gap-2 justify-center h-fit py-2">
<Container
className="flex gap-2 justify-center h-fit py-2"
data-testid="no-search-results-container"
>
<Text>No results found.</Text>
</Container>
)
Expand Down
5 changes: 4 additions & 1 deletion src/modules/search/templates/search-modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ export default function SearchModal() {
indexName={SEARCH_INDEX_NAME}
searchClient={searchClient}
>
<div className="flex absolute flex-col h-fit w-full sm:w-fit">
<div
className="flex absolute flex-col h-fit w-full sm:w-fit"
data-testid="search-modal-container"
>
<div className="w-full flex items-center gap-x-2 p-4 bg-[rgba(3,7,18,0.5)] text-ui-fg-on-color backdrop-blur-2xl rounded-rounded">
<MagnifyingGlassMini />
<SearchBox />
Expand Down

0 comments on commit 1810b9a

Please sign in to comment.