Skip to content
86 changes: 86 additions & 0 deletions core/src/components/item-sliding/test/async/item-sliding.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('item-sliding: async'), () => {
test.beforeEach(async ({ page }) => {
Expand Down Expand Up @@ -35,5 +38,88 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>

await expect(itemSlidingEl).toHaveClass(/item-sliding-active-slide/);
});

test('should not throw errors when adding multiple items with side="end" using the Ionic CDN', async ({
page,
}, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29499',
});

const errors: string[] = [];
page.on('console', (msg) => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
page.on('pageerror', (error) => {
errors.push(error.message);
});

// This issue only happens when using a CDN version of Ionic
// so we need to use the CDN by passing the `importIonicFromCDN` option
// to setContent.
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-title>Item Sliding</ion-title>
<ion-buttons slot="end">
<ion-button id="addItem" onclick="addItem()">ADD ITEM</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list id="list"></ion-list>
</ion-content>

<script>
let itemList = [];
function generateItem() {
const currentItem = itemList.length + 1;
const item = \`
<ion-item-sliding id="item-\${currentItem}">
<ion-item>
<ion-label>Sliding Item \${currentItem}</ion-label>
</ion-item>
<ion-item-options side="end">
<ion-item-option color="danger" id="delete-item-\${currentItem}">Delete</ion-item-option>
</ion-item-options>
</ion-item-sliding>
\`;
itemList.push(item);
return item;
}
function addItem() {
const list = document.getElementById('list');
list.innerHTML += generateItem();
const currentItem = itemList.length;
const deleteId = \`#delete-item-\${currentItem}\`;
const itemId = \`#item-\${currentItem}\`;
document.querySelector(deleteId).addEventListener('click', (ev) => {
document.querySelector(itemId).remove();
});
}
</script>
`,
{ ...config, importIonicFromCDN: true }
);

// Click the button enough times to reproduce the issue
const addButton = page.locator('#addItem');
await addButton.click();
await addButton.click();
await addButton.click();

await page.waitForChanges();

// Check that the items have been added
const items = page.locator('ion-item-sliding');
expect(await items.count()).toBe(3);

// Check that no errors have been logged
expect(errors.length).toBe(0);
});
});
});
19 changes: 17 additions & 2 deletions core/src/utils/test/playwright/page/utils/set-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,33 @@ export const setContent = async (page: Page, html: string, testInfo: TestInfo, o

const baseUrl = process.env.PLAYWRIGHT_TEST_BASE_URL;

// The Ionic bundle is included locally by default unless the test
// config passes in the importIonicFromCDN option. This is useful
// when testing with the CDN version of Ionic.
let ionicImports = `
<link href="${baseUrl}/css/ionic.bundle.css" rel="stylesheet" />
<script type="module" src="${baseUrl}/dist/ionic/ionic.esm.js"></script>
`;

if (options?.importIonicFromCDN) {
ionicImports = `
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js"></script>
`;
}

const output = `
<!DOCTYPE html>
<html dir="${direction}" lang="en">
<head>
<title>Ionic Playwright Test</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="${baseUrl}/css/ionic.bundle.css" rel="stylesheet" />
<link href="${baseUrl}/scripts/testing/styles.css" rel="stylesheet" />
${palette !== 'light' ? `<link href="${baseUrl}/css/palettes/${palette}.always.css" rel="stylesheet" />` : ''}
<script src="${baseUrl}/scripts/testing/scripts.js"></script>
<script type="module" src="${baseUrl}/dist/ionic/ionic.esm.js"></script>
${ionicImports}
<script>
window.Ionic = {
config: {
Expand Down
6 changes: 6 additions & 0 deletions core/src/utils/test/playwright/playwright-declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ interface PageOptions {
* - `'commit'` - consider operation to be finished when network response is received and the document started loading.
*/
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit';

/**
* If true, the default Ionic imports will be included
* via the CDN instead of the local bundle.
*/
importIonicFromCDN?: boolean;
}

export interface E2EPage extends Page {
Expand Down
Loading