Skip to content

Commit

Permalink
updates README and CONTRIBUTING
Browse files Browse the repository at this point in the history
  • Loading branch information
vrknetha committed Dec 7, 2024
1 parent 030e102 commit a54975d
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 54 deletions.
73 changes: 73 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Contributing to playwright-clipboard

We love your input! We want to make contributing to playwright-clipboard as easy and transparent as possible, whether it's:

- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features
- Becoming a maintainer

## We Develop with GitHub
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.

## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html)
Pull requests are the best way to propose changes to the codebase. We actively welcome your pull requests:

1. Fork the repo and create your branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. Make sure your code lints.
6. Issue that pull request!

## Any contributions you make will be under the MIT Software License
In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.

## Report bugs using GitHub's [issue tracker](https://github.com/vrknetha/playwright-clipboard/issues)
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/vrknetha/playwright-clipboard/issues/new); it's that easy!

## Write bug reports with detail, background, and sample code

**Great Bug Reports** tend to have:

- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Give sample code if you can.
- What you expected would happen
- What actually happens
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)

## Development Process

1. Clone the repository:
```bash
git clone https://github.com/vrknetha/playwright-clipboard.git
cd playwright-clipboard
```

2. Install dependencies:
```bash
npm install
```

3. Run tests:
```bash
npm test
```

4. Run linting:
```bash
npm run lint
```

## Testing
We use Playwright Test for our test suite. Please ensure all tests pass before submitting a PR:

```bash
npm test
```

## License
By contributing, you agree that your contributions will be licensed under its MIT License.
222 changes: 168 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,80 +11,166 @@ npm install --save-dev playwright-clipboard
## Features

- Basic clipboard operations (copy, paste, cut)
- Text selection operations
- Word-level operations
- Clipboard content management
- Cross-browser compatibility
- TypeScript support
- Rich text operations with HTML preservation
- Text selection operations with character and word-level control
- Word-level operations for precise text manipulation
- Clipboard content management with direct access
- Cross-browser compatibility (Chromium, Firefox, WebKit)
- TypeScript support with full type definitions
- Comprehensive error handling
- Fallback mechanisms for browser-specific limitations

## Usage
## Examples

### Basic Operations

```typescript
import { test } from '@playwright/test';
import { test, expect } from '@playwright/test';
import { PlaywrightClipboard } from 'playwright-clipboard';

test('basic clipboard operations', async ({ page }) => {
test('basic copy/paste operations', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);

// Copy text from source

await page.goto('http://localhost:8080');

// Copy from source input and paste to target
await clipboard.copy('#source');
await clipboard.paste('#target');

const result = await page.inputValue('#target');
expect(result).toBe('Hello World');
});

test('cut operations', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
const initialText = 'Test Content';

// Paste text to target
await page.fill('#source', initialText);
await clipboard.cut('#source');
await clipboard.paste('#target');

// Cut text
await clipboard.cut('#editor');
// Source should be empty after cut
const sourceContent = await page.inputValue('#source');
expect(sourceContent).toBe('');

// Get clipboard content
const content = await clipboard.getClipboardContent();
// Target should have the cut content
const targetContent = await page.inputValue('#target');
expect(targetContent).toBe(initialText);
});
```

### Rich Text Operations

// Set clipboard content
await clipboard.setClipboardContent('Hello World');
```typescript
test('rich text operations', async ({ page, browserName }) => {
const clipboard = new PlaywrightClipboard(page);

// Copy rich text content
await clipboard.copyRichText('#richSource');
await clipboard.pasteRichText('#richTarget');

const result = await page.$eval('#richTarget', el => el.innerHTML.trim());

if (browserName === 'webkit') {
// WebKit may handle rich text differently
const plainText = await page.$eval('#richTarget',
el => el.textContent?.trim() || '');
expect(plainText).toBe('This is bold text');
} else {
// Other browsers preserve HTML structure
expect(result).toContain('<b>bold</b>');
}
});
```

### Word-Level Operations
### Text Selection

```typescript
test('word-level operations', async ({ page }) => {
test('text selection', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
const testText = 'Select this text';

// Copy specific words
await clipboard.copyBetweenWords('#editor', 1, 3);
await page.fill('#text', testText);
await clipboard.select('#text', 7, 11); // Selects "this"

const selectedText = await clipboard.getSelectedText();
expect(selectedText).toBe('this');
});
```

### Word Operations

// Paste after a specific word
await clipboard.pasteAfterWord('#editor', 2);
```typescript
test('word operations', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
const testText = 'The quick brown fox jumps';

// Paste before a specific word
await clipboard.pasteBeforeWord('#editor', 0);
await page.fill('#editor', testText);
await clipboard.copyBetweenWords('#editor', 2, 3); // Copy "brown fox"
await clipboard.paste('#target');

// Replace a word
await clipboard.replaceWord('#editor', 1);
const targetContent = await page.inputValue('#target');
expect(targetContent).toBe('brown fox');
});
```

### Selection Operations
### Special Characters and Multiline Text

```typescript
test('selection operations', async ({ page }) => {
test('special characters', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
const testText = 'Special @#$% characters!';

// Select all text
await clipboard.selectAll('#editor');
await page.fill('#source', testText);
await clipboard.copy('#source');
await clipboard.paste('#target');

const result = await page.inputValue('#target');
expect(result).toBe(testText);
});

// Select specific range
await clipboard.select('#editor', 0, 10);
test('multiline text', async ({ page }) => {
const clipboard = new PlaywrightClipboard(page);
const testText = 'Line 1\nLine 2\nLine 3';

// Select word range
await clipboard.selectWordRange('#editor', 1, 3);
await page.fill('#editor', testText);
await clipboard.copy('#editor');
await clipboard.paste('#target');

// Get selected text
const selectedText = await clipboard.getSelectedWords();
const result = await page.inputValue('#target');
expect(result).toBe(testText);
});
```

## Browser Support

| Feature | Chromium | Firefox | WebKit (Safari) |
|---------|----------|---------|-----------------|
| Basic Operations | Native Clipboard API | Keyboard Shortcuts | Clipboard API + Fallback |
| Rich Text | Full Support | Full Support | Full Support* |
| Word Operations | Full Support | Full Support | Full Support |

\* Uses `execCommand` fallback for some operations

## Technical Details

The package implements several fallback mechanisms to ensure consistent behavior across browsers:

1. **Clipboard Access**:
- Primary: Native Clipboard API
- Fallback: Keyboard shortcuts (Meta+C, Meta+V, Meta+X)
- Last Resort: execCommand for WebKit

2. **Text Selection**:
- Input/Textarea: Uses `setSelectionRange`
- Rich Text: Uses `Range` and `Selection` APIs
- Word-Level: Custom boundary detection

3. **Rich Text Handling**:
- Preserves HTML structure where supported
- Graceful degradation to plain text
- Browser-specific optimizations

## API Reference

### Constructor
Expand All @@ -93,41 +179,69 @@ test('selection operations', async ({ page }) => {
constructor(page: Page, options?: ClipboardOptions)
```

Options:
- `timeout?: number` - Operation timeout in milliseconds (default: 5000)

### Methods

- `copy(selector: string): Promise<void>`
- `paste(selector: string): Promise<void>`
- `cut(selector: string): Promise<void>`
- `getClipboardContent(): Promise<string>`
- `setClipboardContent(text: string): Promise<void>`
- `selectAll(selector: string): Promise<void>`
- `select(selector: string, start: number, end: number): Promise<void>`
#### Basic Operations
- `copy(selector: string): Promise<void>` - Copy content from element
- `paste(selector: string): Promise<void>` - Paste content to element
- `cut(selector: string): Promise<void>` - Cut content from element

#### Rich Text Operations
- `copyRichText(selector: string): Promise<void>` - Copy with HTML preservation
- `pasteRichText(selector: string): Promise<void>` - Paste with HTML preservation
- `cutRichText(selector: string): Promise<void>` - Cut with HTML preservation

#### Selection Operations
- `selectAll(selector: string): Promise<void>` - Select all content
- `select(selector: string, start: number, end: number): Promise<void>` - Select range
- `getSelectedText(): Promise<string>` - Get selected text

#### Word Operations
- `copyBetweenWords(selector: string, startWordIndex: number, endWordIndex: number): Promise<void>`
- `pasteAfterWord(selector: string, wordIndex: number): Promise<void>`
- `pasteBeforeWord(selector: string, wordIndex: number): Promise<void>`
- `replaceWord(selector: string, wordIndex: number): Promise<void>`
- `selectWordRange(selector: string, startIndex: number, endIndex: number): Promise<void>`
- `getSelectedWords(): Promise<string>`
- `getWordBoundaries(selector: string, wordIndex: number): Promise<{ start: number; end: number }>`

#### Clipboard Management
- `getClipboardContent(): Promise<string>` - Get current clipboard content
- `setClipboardContent(text: string): Promise<void>` - Set clipboard content

## Error Handling

The package includes comprehensive error handling with specific error types:

- `COPY_ERROR`
- `PASTE_ERROR`
- `CLIPBOARD_ACCESS_DENIED`
- `SELECTION_FAILED`
- `INVALID_WORD_INDEX`
- `WORD_BOUNDARY_ERROR`
- `EMPTY_SELECTION`
- `PASTE_POSITION_ERROR`
```typescript
export enum ClipboardError {
COPY_ERROR = 'Copy operation failed',
PASTE_ERROR = 'Paste operation failed',
CLIPBOARD_ACCESS_DENIED = 'Cannot access clipboard',
SELECTION_FAILED = 'Text selection failed',
INVALID_WORD_INDEX = 'Invalid word index specified',
WORD_BOUNDARY_ERROR = 'Cannot determine word boundaries',
EMPTY_SELECTION = 'No text selected',
PASTE_POSITION_ERROR = 'Invalid paste position'
}
```

## Requirements

- Node.js >= 18.0.0
- Playwright >= 1.40.0
- TypeScript >= 4.5.0 (for TypeScript users)

## Contributing

Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.

## License

MIT
MIT

## Support

For bugs and feature requests, please [open an issue](https://github.com/vrknetha/playwright-clipboard/issues).

0 comments on commit a54975d

Please sign in to comment.