npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sellsuki-org/sellsuki-components-test

v1.0.3

Published

Playwright test helpers for @sellsuki-org/sellsuki-components

Downloads

0

Readme

@sellsuki-org/sellsuki-components-test

test function กลางของ ssk- component* — คู่กับ @sellsuki-org/sellsuki-components

sellsuki-components เก็บ component ให้ dev ใช้ → repo นี้เก็บ test function ของ component เดียวกัน ให้ QA ใช้

ทุกทีม install จากที่นี่ เพิ่ม / ลด / แก้ ทำที่นี่ที่เดียว (ดู CONTRIBUTING.md)

ติดตั้ง

pin แบบเป๊ะเสมอ (--save-exact) เพื่อให้ทุกทีมได้ version เดียวกัน

npm i -D --save-exact @sellsuki-org/sellsuki-components-test
"@sellsuki-org/sellsuki-components-test": "1.0.0"   // ไม่มี ^

ทำไมต้อง exact: ^1.0.0 แปลว่า "1.0.0 ขึ้นไปแต่ต่ำกว่า 2.0.0" → ทีมที่ install ต่างเวลากันจะได้ตัวคนละเวอร์ชัน ทั้งที่ package.json เขียนเหมือนกันเป๊ะ → เปิดเทียบกันแล้วเข้าใจผิดว่าตรงกัน

อัปเดตเมื่อพร้อม (แต่ละทีมทำเอง ไม่มี auto-update):

npm i -D --save-exact @sellsuki-org/[email protected]
npm ls @sellsuki-org/sellsuki-components-test   # เช็คว่าจริง ๆ ใช้ตัวไหนอยู่

แล้ว commit package.json + package-lock.json — CI ที่ใช้ npm ci จะยึด lock เพราะฉะนั้น version ใหม่จะเข้า suite ก็ต่อเมื่อมีคน commit lock ใหม่

@playwright/test เป็น peerDependency (>=1.56) — ใช้ตัวที่ repo คุณมีอยู่แล้ว

Versioning

version ของ package นี้เป็นของตัวเอง ไม่ล้อกับ sellsuki-components

เพราะสองอย่างนี้ขับเคลื่อนด้วยคนละเรื่อง — API ของ helper โตตามที่ QA เพิ่ม function ส่วน component library ปล่อยของตามงานของทีม dev ถ้ามัดเลขไว้ด้วยกันจะเกิด release ที่ไม่มีความหมาย (dev ปล่อย minor แต่ helper ไม่ได้แก้อะไร) และเพิ่ม function ใหม่แล้ว bump minor ไม่ได้เพราะ minor ถูกจองไว้

semver ตามความหมายจริง:

| เปลี่ยนอะไร | bump | |---|---| | เพิ่ม helper ตัวใหม่ / เพิ่มเมธอดใน helper เดิม | minor | | แก้ selector ให้ตรง DOM ที่เปลี่ยน / แก้บั๊ก | patch | | เปลี่ยนชื่อ / ลบเมธอด / เปลี่ยน signature | major |

ความเข้ากันได้กับ sellsuki-components

ประกาศไว้ใน peerDependencies (optional) ให้ npm เตือนเอง ไม่ต้องพึ่งตารางที่ไม่มีใครอ่าน:

"peerDependencies": {
  "@sellsuki-org/sellsuki-components": ">=0.24.0"
}

ไม่ล็อกเพดานเพื่อไม่ให้บล็อกทีมที่อัป component ก่อน แต่ verified แค่ช่วงนี้:

| helper | ทดสอบกับ components | หมายเหตุ | |---|---|---| | 1.x | 0.24.x | เขียนจาก source ณ 0.24.6 · e2e ทั้งสอง repo อยู่ ^0.24.7/^0.24.8 → สูงสุด 0.24.8 | | — | 0.250.27 | ยังไม่ตรวจ (npm ล่าสุดคือ 0.27.0) ทีมไหนอัป component ก่อนช่วยรัน suite แล้วแจ้งด้วย |

เวลามีใครอัป component ข้าม minor ให้ไล่ดูว่า DOM ที่ helper ยึดเปลี่ยนไหม แล้วอัปเดตตารางนี้ + ปล่อย patch ถ้าต้องแก้ selector

ใช้งาน

import { ToastComponent, GenericTableAssert, SskInputComponent } from '@sellsuki-org/sellsuki-components-test';

const toast = new ToastComponent(page);
await toast.assertSuccess();
await toast.assertMessage('บันทึกสำเร็จ');

const table = new GenericTableAssert(page.getByTestId('order.table'), page.locator('ssk-table-cell'), 6);
await table.assertRowCount(10);

await SskInputComponent.fill(page.getByTestId('amount').locator('input'), '1500');

Inventory

รวมมาจาก resources/ssk-component/ ของ sellsuki-e2e-test-playwright (CCS3) และ patona-e2e-test-playwright — 17 + 19 ไฟล์ → 23 ไฟล์ 24 คลาส (รวม export ทั้งหมด 27 ตัว = 24 คลาส + DEFAULT_TIMEOUT + ms() + exactTextWithOuterWhitespace())

src/components/ — wrapper ของ ssk-* element

| ไฟล์ | export | element | มาจาก | |---|---|---|---| | ssk-badge.component.ts | SskBadgeComponent | ssk-badge | S + P | | ssk-button.component.ts | SskButtonComponent | ssk-button | P | | ssk-calendar.component.ts | SskCalendarComponent | ssk-calendar | P | | ssk-checkbox.component.ts | SskCheckboxComponent | ssk-checkbox | S | | ssk-confirmation-modal.component.ts | ConfirmationModalComponent | ssk-modal | S + P | | ssk-date-picker.component.ts | SskDatePickerComponent, SskRangeDatePickerComponent | ssk-date-picker, ssk-range-date-picker | S + P | | ssk-dropdown.component.ts | SskDropdownComponent | ssk-dropdown | S + P | | ssk-input.component.ts | SskInputComponent | ssk-input | S + P | | ssk-modal.component.ts | SskModalComponent | ssk-modal (testid-based) | P | | ssk-pagination.component.ts | PaginationComponent | ssk-pagination | S + P | | ssk-radio.component.ts | SskRadioComponent | ssk-radio | S | | ssk-radio-group.component.ts | SskRadioGroupComponent | ssk-radio-group | P | | ssk-sidebar.component.ts | SskSidebarComponent | ssk-sidebar | S + P | | ssk-tab.component.ts | SskTabComponent | ssk-tabs, ssk-tab-button | S + P | | ssk-table.component.ts | GenericTableAssert | ssk-table, ssk-dynamic-table | S + P | | ssk-text.component.ts | SskTextComponent | ssk-text | P | | ssk-textarea.component.ts | SskTextareaComponent | ssk-textarea | P | | ssk-time-picker.component.ts | SskTimePickerComponent | ssk-time | S + P | | ssk-toast.component.ts | ToastComponent | ssk-toast | S + P | | ssk-toggle.component.ts | SskToggleComponent | ssk-toggle | S | | ssk-tooltip.component.ts | SskTooltipComponent | ssk-tooltip | S | | ssk-top-navbar.component.ts | SskTopNavbarComponent | ssk-top-navbar | P | | ssk-upload-image.component.ts | SskUploadImageComponent | widget อัปโหลดรูป | S |

helper กลาง: DEFAULT_TIMEOUT, ms(), exactTextWithOuterWhitespace()

ขอบเขต: อะไรไม่เข้า package นี้

บล็อก UI ที่แอปประกอบขึ้นเอง ไม่เข้า — เก็บไว้ที่ e2e repo ของแอปนั้น เพราะมัน version ตามแอป ไม่ใช่ตาม sellsuki-components (ดูเหตุผลเต็มใน CONTRIBUTING.md)

ตัวอย่างที่พิจารณาแล้วให้อยู่นอก package: หน้า "ไม่มีสิทธิ์เข้าถึง" dev ทำ src/components/PermissionDenied.svelte แยกกันในแต่ละแอป (CCS3 ใช้ 7 หน้า / OMS ใช้ 10 หน้า) และ ssk-wave-icon ที่มันใช้ปรากฏ แค่ที่นี่ที่เดียวต่อแอป → ไม่มี element กลางให้ wrap

เมธอดที่เปลี่ยนชื่อ

ตอนรวมสอง repo มีเมธอด ชื่อซ้ำแต่ความหมายคนละอย่าง จึงตั้งชื่อใหม่ให้บอกชัดว่าเช็คอะไร ชื่อเดิมที่ไม่กำกวมยังอยู่ครบ

| component | ของเดิม | repo | ของใหม่ | |---|---|---|---| | ToastComponent | assertVisible(message) | S | assertMessage(message) | | ToastComponent | assertVisible(testId) | P | assertVisibleByTestId(testId) | | ToastComponent | assertDismissed(message) | S | assertMessageDismissed(message) | | SskBadgeComponent | assertText(container, text) | S | assertTextIn(container, text) | | SskInputComponent | assertError(errorNode, msg) | S | assertErrorNodeText(errorNode, msg) | | SskInputComponent | assertErrorCleared(errorNode) | S | assertErrorNodeHidden(errorNode) | | SskDropdownComponent | assertSelectedText(text) (เช็คใน ssk-dropdown-button) | P | assertSelectedTextInButton(text) | | GenericTableAssert | assertRowCountIs(n) | P | assertRowCount(n) — ชื่อเดิมยังใช้ได้ (deprecated) | | GenericTableAssert | assertCellTextAt(row, col0, text) | P | assertCellText(row, col1, text)col เปลี่ยนเป็น 1-based ชื่อเดิมยังใช้ได้ (deprecated) | | GenericTableAssert | clickCellAt(row, col0) | P | clickCell(row, col1) — ชื่อเดิมยังใช้ได้ (deprecated) | | ConfirmationModalComponent | default text จาก i18n | S | ต้องส่ง title / content เข้ามาเอง — package กลางไม่ import i18n |

timeout เดิมของ patona บางเมธอดเป็น 15s ตอนนี้ default เป็น 30s ทุกเมธอด และ override ได้ด้วย { timeout: 15_000 } เป็น argument สุดท้าย

Migration จากโฟลเดอร์ resources/ssk-component/ เดิม

import จาก package ตรง ๆ แล้วลบไฟล์เดิม — ไม่ต้องทำ shim เหตุผล: โฟลเดอร์ที่เหลือค้างไว้จะดูเหมือนแก้ได้ แล้วสุดท้ายจะมีคนแก้ที่นั่นแทนที่จะมาแก้ที่นี่ ซึ่งพาเรากลับไปสู่ปัญหาเดิม — ถ้าไฟล์ไม่มีอยู่ กฎมันบังคับตัวเอง

# 1
npm i -D @sellsuki-org/sellsuki-components-test

# 2 — rewrite import ทุกจุดให้ชี้ package (path ลึกกี่ชั้นก็ครอบ)
grep -rl "ssk-component/ssk-" resources tests --include='*.ts' --include='*.js' \
  | xargs sed -i '' -E "s|from '(\.\./)*ssk-component/ssk-[a-z-]+\.component'|from '@sellsuki-org/sellsuki-components-test'|g"

# 3 — ลบไฟล์ที่ย้ายเข้ามาอยู่ใน package แล้ว
#     ⚠️ เหลือไว้เฉพาะไฟล์ที่เป็นของแอปเอง (ดูตารางล่าง)

# 4 — ให้ compiler ไล่จุดที่ใช้เมธอดชื่อเก่า
npx tsc --noEmit

# 5 — แก้ตามที่ฟ้อง (ตาราง "เมธอดที่เปลี่ยนชื่อ" ข้างบน) แล้วรัน suite
ENV=staging npx playwright test

ไฟล์ที่ต้องเหลือไว้ที่ repo — และควรย้ายออกจาก ssk-component/

2 ไฟล์นี้ ไม่ใช่ wrapper ของ ssk-* element จึงไม่ควรอยู่ในโฟลเดอร์ชื่อ ssk-component/ ย้ายไป resources/pages/common/ แล้วตัด prefix ssk- ออก (โฟลเดอร์นั้นมี precedent อยู่แล้ว เช่น side-nav.ts = บล็อก UI ที่หลายหน้าใช้ร่วมกันแต่เป็นของแอปเอง — ชนิดเดียวกันเลย)

| ที่อยู่ใหม่ | ทำไมไม่เข้า package | |---|---| | resources/pages/common/permission-denied.ts | บล็อก UI ที่แอปประกอบเอง version ตามแอป ไม่ใช่ตาม library | | resources/pages/common/confirmation-modal.ts | เป็น wrapper ที่ใส่ default i18n ให้ตัวใน package — i18n ต้องอยู่ที่ repo | | UNSAVED_ALERT_TESTID (patona) | เป็น testid ของแอป ('unsaved-alert' จาก Layout.svelte ของ Seller Center) ไม่ใช่สัญญาของ component — ย้ายไปไว้ resources/const/ แล้ว import จากที่นั่น (ใช้ 5 page object) |

จบแล้ว resources/ssk-component/ ควร ไม่มีอยู่ เพื่อไม่ให้ใครเข้าใจผิดว่าแก้ helper ที่นั่นได้

// resources/pages/common/confirmation-modal.ts
import { ConfirmationModalComponent as Base, type ConfirmationModalOptions }
  from '@sellsuki-org/sellsuki-components-test';
import { CCS3_CONFIRMATION_MODAL_i18N } from '../../../variables/i18n/component';
import type { Page, Locator } from '@playwright/test';

const i18n = CCS3_CONFIRMATION_MODAL_i18N[process.env.LANGUAGE || 'th'];

// logic อยู่ที่ package / i18n อยู่ที่ repo — ตามกฎ "component ห้าม import i18n"
export class ConfirmationModalComponent extends Base {
    constructor(target: Page | Locator, options: ConfirmationModalOptions = {}) {
        super(target, {
            title: i18n.confirmation_modal_title,
            content: i18n.confirmation_modal_content,
            cancelButton: i18n.confirmation_modal_cancel_button,
            confirmButton: i18n.confirmation_modal_exit_button,
            ...options,
        });
    }
}

new ConfirmationModalComponent(page) แบบไม่ส่ง options ยังคอมไพล์ผ่าน แต่จะเลิกเช็คข้อความเงียบ ๆ — tsc จับไม่ได้ ต้องทำ wrapper นี้ก่อนเสมอ

พัฒนา

npm ci
npm run build       # tsup → dist/ (ESM + CJS + .d.ts)
npm run typecheck

repo นี้ ไม่มี test ของตัวเอง — การยืนยันว่า selector ยังตรงกับ DOM จริงเกิดที่ suite ของแต่ละทีมอยู่แล้ว และ dev แจ้งล่วงหน้าเมื่อมีการแก้ component ทำให้ QA รู้ว่าต้อง recheck ส่วนไหน

⚠️ ข้อจำกัดที่ต้องรู้: typecheck และ build จับ selector ที่ผิดไม่ได้ เพราะ locator เป็น string (ทดสอบแล้ว — แก้ toHaveAttribute('type') เป็น 'variant' ทั้ง typecheck และ build ยังผ่าน) เพราะฉะนั้นเวลาแก้ helper ให้รัน suite ของ repo ที่ใช้จริงอย่างน้อยหนึ่งตัวก่อน merge

ทำไมต้อง build เป็น dual ESM + CJS

  • Playwright ไม่ transpile ไฟล์ใน node_modules → ship .ts ดิบไม่ได้
  • e2e repo ปลายทางไม่มี "type": "module" → test ถูกโหลดแบบ CJS → ESM-only เสี่ยงพัง

CI

มี job เดียว — publish_npm (มาจาก shared pipeline) ทำงานเมื่อ push tag vX.Y.Z รัน npm cinpm version <tag>npm publish ขึ้น registry.npmjs.org

build ถูกรันโดย prepublishOnly ตอน publish อยู่แล้ว → build พัง = publish ไม่ออก

Release

version ใน package.json ปล่อยเป็น 0.0.0 — CI เขียนทับจาก tag (npm version $CI_COMMIT_TAG)

git tag v1.0.0
git push origin v1.0.0

tag ต้องตรง ^v\d{1,2}\.\d{1,2}\.\d{1,2}$prerelease อย่าง v1.0.0-beta.1 pipeline จะไม่ทำงาน