diff --git a/.github/workflows/reusable-app-prod.yml b/.github/workflows/reusable-app-prod.yml
index d560e6a8a43..8458f2a0fdf 100644
--- a/.github/workflows/reusable-app-prod.yml
+++ b/.github/workflows/reusable-app-prod.yml
@@ -213,7 +213,7 @@ jobs:
       fail-fast: false
       matrix:
         # List string expressions that is comma separated ids of tests in "test/cypress/integration"
-        spec-group: ['21', '23', '30', '50']
+        spec-group: ['21', '30', '50']
 
     services:
       mongodb:
diff --git a/apps/app/test/cypress/e2e/23-editor/assets/example.txt b/apps/app/playwright/23-editor/assets/example.txt
similarity index 100%
rename from apps/app/test/cypress/e2e/23-editor/assets/example.txt
rename to apps/app/playwright/23-editor/assets/example.txt
diff --git a/apps/app/playwright/23-editor/with-navigation.spec.ts b/apps/app/playwright/23-editor/with-navigation.spec.ts
new file mode 100644
index 00000000000..58650486964
--- /dev/null
+++ b/apps/app/playwright/23-editor/with-navigation.spec.ts
@@ -0,0 +1,113 @@
+import { readFileSync } from 'fs';
+import path from 'path';
+
+import { test, expect, type Page } from '@playwright/test';
+
+/**
+ * for the issues:
+ * @see https://redmine.weseek.co.jp/issues/122040
+ * @see https://redmine.weseek.co.jp/issues/124281
+ */
+test('should not be cleared and should prevent GrantSelector from modified', async({ page }) => {
+  await page.goto('/Sandbox/for-122040');
+
+  // Open Editor
+  await page.getByTestId('editor-button').click();
+  await expect(page.getByTestId('grw-editor-navbar-bottom')).toBeVisible();
+
+  // Open GrantSelector and select "only me"
+  await page.getByTestId('grw-grant-selector').click();
+  const dropdownMenu = page.getByTestId('grw-grant-selector-dropdown-menu');
+  await expect(dropdownMenu).toBeVisible();
+  await dropdownMenu.locator('.dropdown-item').nth(2).click();
+  await expect(page.getByTestId('grw-grant-selector')).toContainText('Only me');
+
+  // Upload attachment
+  const filePath = path.resolve(__dirname, '../23-editor/assets/example.txt');
+  const buffer = readFileSync(filePath).toString('base64');
+  const dataTransfer = await page.evaluateHandle(
+    async({ bufferData, localFileName, localFileType }) => {
+      const dt = new DataTransfer();
+
+      const blobData = await fetch(bufferData).then(res => res.blob());
+
+      const file = new File([blobData], localFileName, {
+        type: localFileType,
+      });
+      dt.items.add(file);
+      return dt;
+    },
+    {
+      bufferData: `data:application/octet-stream;base64,${buffer}`,
+      localFileName: 'sample.tst',
+      localFileType: 'application/octet-stream',
+    },
+  );
+  await page.locator('.dropzone').first().dispatchEvent('drop', { dataTransfer });
+  await expect(page.getByTestId('page-editor-preview-body').getByTestId('rich-attachment')).toBeVisible();
+
+  // Save page
+  await page.getByTestId('save-page-btn').click();
+
+  // Expect grant not to be reset after uploading an attachment
+  await expect(page.getByTestId('page-grant-alert')).toContainText('Browsing of this page is restricted');
+});
+
+const appendTextToEditorUntilContains = async(page: Page, text: string) => {
+  await page.locator('.cm-content').fill(text);
+  await expect(page.getByTestId('page-editor-preview-body')).toContainText(text);
+};
+
+/**
+ * for the issue:
+ * @see https://redmine.weseek.co.jp/issues/115285
+ */
+test('Successfully updating the page body', async({ page }) => {
+  const page1Path = '/Sandbox/for-115285/page1';
+  const page2Path = '/Sandbox/for-115285/page2';
+
+  const page1Body = 'Hello';
+  const page2Body = 'World';
+
+
+  await page.goto(page1Path);
+
+  // Open Editor (page1)
+  await page.getByTestId('editor-button').click();
+  await expect(page.getByTestId('grw-editor-navbar-bottom')).toBeVisible();
+
+  // Append text
+  await appendTextToEditorUntilContains(page, page1Body);
+
+  // Save page
+  await page.getByTestId('save-page-btn').click();
+
+  await expect(page.locator('.main')).toContainText(page1Body);
+
+  // Duplicate page1
+  await page.getByTestId('grw-contextual-sub-nav').getByTestId('open-page-item-control-btn').click();
+  await page.getByTestId('open-page-duplicate-modal-btn').click();
+  await expect(page.getByTestId('page-duplicate-modal')).toBeVisible();
+  await page.locator('.form-control').fill(page2Path);
+  await page.getByTestId('btn-duplicate').click();
+
+  // Open Editor (page2)
+  await page.getByTestId('editor-button').click();
+  await expect(page.getByTestId('grw-editor-navbar-bottom')).toBeVisible();
+
+  // Expect to see the text from which you are duplicating
+  await expect(page.getByTestId('page-editor-preview-body')).toContainText(page1Body);
+
+  // Append text
+  await appendTextToEditorUntilContains(page, page1Body + page2Body);
+
+
+  await page.goto(page1Path);
+
+  // Open Editor (page1)
+  await page.getByTestId('editor-button').click();
+  await expect(page.getByTestId('grw-editor-navbar-bottom')).toBeVisible();
+
+  await expect(page.getByTestId('page-editor-preview-body')).toContainText(page1Body);
+
+});
diff --git a/apps/app/src/client/components/ReactMarkdownComponents/RichAttachment.tsx b/apps/app/src/client/components/ReactMarkdownComponents/RichAttachment.tsx
index 94ecf9f57e3..a939b1bfa67 100644
--- a/apps/app/src/client/components/ReactMarkdownComponents/RichAttachment.tsx
+++ b/apps/app/src/client/components/ReactMarkdownComponents/RichAttachment.tsx
@@ -53,7 +53,7 @@ export const RichAttachment = React.memo((props: RichAttachmentProps) => {
   }
 
   return (
-    <div className={`${styles.attachment} d-inline-block`}>
+    <div data-testid="rich-attachment" className={`${styles.attachment} d-inline-block`}>
       <div className="my-2 p-2 card">
         <div className="p-1 card-body d-flex align-items-center">
           <div className="me-2 px-0 d-flex align-items-center justify-content-center">
diff --git a/apps/app/src/components/PageView/PageAlerts/PageGrantAlert.tsx b/apps/app/src/components/PageView/PageAlerts/PageGrantAlert.tsx
index e6943ee6a84..8436a7a972b 100644
--- a/apps/app/src/components/PageView/PageAlerts/PageGrantAlert.tsx
+++ b/apps/app/src/components/PageView/PageAlerts/PageGrantAlert.tsx
@@ -55,7 +55,7 @@ export const PageGrantAlert = (): JSX.Element => {
 
 
   return (
-    <p className="alert alert-primary py-3 px-4">
+    <p data-testid="page-grant-alert" className="alert alert-primary py-3 px-4">
       {renderAlertContent()}
     </p>
   );
diff --git a/apps/app/test/cypress/e2e/23-editor/23-editor--with-navigation.cy.ts b/apps/app/test/cypress/e2e/23-editor/23-editor--with-navigation.cy.ts
deleted file mode 100644
index ffdbf915a2e..00000000000
--- a/apps/app/test/cypress/e2e/23-editor/23-editor--with-navigation.cy.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import path from 'path-browserify';
-
-function openEditor() {
-  cy.get('#grw-page-editor-mode-manager').as('pageEditorModeManager').should('be.visible');
-  cy.getByTestid('editor-button').click();
-  cy.getByTestid('grw-editor-navbar-bottom').should('be.visible');
-  cy.get('.cm-content').should('be.visible');
-}
-
-context('Editor while uploading to a new page', () => {
-
-  const ssPrefix = 'editor-while-uploading-';
-
-  beforeEach(() => {
-    // login
-    cy.fixture("user-admin.json").then(user => {
-      cy.login(user.username, user.password);
-    });
-  });
-
-  /**
-   * for the issues:
-   * @see https://redmine.weseek.co.jp/issues/122040
-   * @see https://redmine.weseek.co.jp/issues/124281
-   */
-  it('should not be cleared and should prevent GrantSelector from modified', { scrollBehavior: false }, () => {
-    cy.visit('/Sandbox/for-122040');
-
-    openEditor();
-
-    cy.screenshot(`${ssPrefix}-prevent-grantselector-modified-1`);
-
-    // input the body
-    const body = 'Hello World!';
-    cy.get('.cm-content').should('be.visible').type(body, { force: true });
-    cy.getByTestid('page-editor-preview-body').should('contain.text', body);
-
-    // open GrantSelector
-    cy.waitUntil(() => {
-      // do
-      cy.getByTestid('grw-grant-selector').within(() => {
-        cy.get('button.dropdown-toggle').click({force: true});
-      });
-      // wait until
-      return cy.getByTestid('grw-grant-selector-dropdown-menu').then($elem => $elem.is(':visible'))
-    });
-
-    // Select "Only me"
-    cy.getByTestid('grw-grant-selector-dropdown-menu').find('.dropdown-item').should('have.length', 4).then((menuItems) => {
-      // click "Only me"
-      menuItems[2].click();
-    })
-
-    cy.getByTestid('grw-grant-selector').find('.dropdown-toggle').should('contain.text', 'Only me');
-    cy.screenshot(`${ssPrefix}-prevent-grantselector-modified-2`);
-
-    // intercept API req/res for fixing labels
-    const dummyAttachmentId = '64b000000000000000000000';
-    let uploadedAttachmentId = '';
-    cy.intercept('POST', '/_api/v3/attachment', (req) => {
-      req.continue((res) => {
-        // store the attachment id
-        uploadedAttachmentId = res.body.attachment._id;
-        // overwrite filePathProxied
-        res.body.attachment.filePathProxied = `/attachment/${dummyAttachmentId}`;
-      });
-    }).as('attachmentsAdd');
-    cy.intercept('GET', `/_api/v3/attachment?attachmentId=${dummyAttachmentId}`, (req) => {
-      // replace attachmentId query
-      req.url = req.url.replace(dummyAttachmentId, uploadedAttachmentId);
-      req.continue((res) => {
-        // overwrite the attachment createdAt
-        res.body.attachment.createdAt = new Date('2023-07-01T00:00:00');
-      });
-    });
-
-    // drag-drop a file
-    const filePath = path.relative('/', path.resolve(Cypress.spec.relative, '../assets/example.txt'));
-    cy.get('.dropzone').eq(0).selectFile(filePath, { action: 'drag-drop' });
-    cy.wait('@attachmentsAdd');
-
-    cy.screenshot(`${ssPrefix}-prevent-grantselector-modified-3`);
-
-    // Update page using shortcut keys
-    cy.get('.cm-content').click({force: true}).type('{ctrl+s}');
-
-    cy.screenshot(`${ssPrefix}-prevent-grantselector-modified-4`);
-
-    // expect
-    cy.get('.Toastify__toast').should('contain.text', 'Saved successfully');
-    cy.get('.cm-content').should('contain.text', body);
-    cy.get('.cm-content').should('contain.text', '[example.txt](/attachment/64b000000000000000000000');
-    cy.getByTestid('grw-grant-selector').find('.dropdown-toggle').should('contain.text', 'Only me');
-    cy.screenshot(`${ssPrefix}-prevent-grantselector-modified-5`);
-  });
-
-});
-
-context('Editor while navigation', () => {
-
-  const ssPrefix = 'editor-while-navigation-';
-
-  beforeEach(() => {
-    // login
-    cy.fixture("user-admin.json").then(user => {
-      cy.login(user.username, user.password);
-    });
-  });
-
-  /**
-   * for the issue:
-   * @see https://redmine.weseek.co.jp/issues/115285
-   */
-  it('Successfully updating the page body', { scrollBehavior: false }, () => {
-    const page1Path = '/Sandbox/for-115285/page1';
-    const page2Path = '/Sandbox/for-115285/page2';
-
-    cy.visit(page1Path);
-
-    openEditor();
-
-    // page1
-    const bodyHello = 'hello';
-    cy.get('.cm-content').should('be.visible').type(bodyHello, { force: true });
-    cy.getByTestid('page-editor-preview-body').should('contain.text', bodyHello);
-    cy.get('.cm-content').screenshot(`${ssPrefix}-editor-for-page1`);
-
-    // save page1
-    cy.getByTestid('save-page-btn').click();
-
-    // open duplicate modal
-    cy.waitUntil(() => {
-      // do
-      cy.getByTestid('grw-contextual-sub-nav').within(() => {
-        cy.getByTestid('open-page-item-control-btn').find('button').click({force: true});
-      });
-      // wait until
-      return cy.getByTestid('page-item-control-menu').then($elem => $elem.is(':visible'))
-    });
-    cy.getByTestid('open-page-duplicate-modal-btn').filter(':visible').click({force: true});
-
-    // duplicate and navigate to page1
-    cy.getByTestid('page-duplicate-modal').should('be.visible').within(() => {
-      cy.get('input.form-control').clear();
-      cy.get('input.form-control').type(page2Path);
-      cy.getByTestid('btn-duplicate').click();
-    })
-
-    openEditor();
-    cy.get('.cm-content').screenshot(`${ssPrefix}-editor-for-page2`);
-
-    // type (without save)
-    const bodyWorld = ' world!!'
-    cy.get('.cm-content').should('be.visible').type(`{moveToEnd}${bodyWorld}`, { force: true });
-    cy.getByTestid('page-editor-preview-body').should('contain.text', `${bodyHello}${bodyWorld}`);
-    cy.get('.cm-content').screenshot(`${ssPrefix}-editor-for-page2-modified`);
-
-    // create a link to page1
-    cy.get('.cm-content').type('\n\n[page1](./page1)');
-
-    // go to page1
-    cy.getByTestid('page-editor-preview-body').within(() => {
-      cy.get("a:contains('page1')").click();
-    });
-
-    openEditor();
-
-    cy.get('.cm-content').screenshot(`${ssPrefix}-editor-for-page1-returned`);
-
-    // expect
-    cy.get('.cm-content').should('contain.text', bodyHello);
-    cy.get('.cm-content').should('not.contain.text', bodyWorld); // text that added to page2
-    cy.get('.cm-content').should('not.contain.text', 'page1'); // text that added to page2
-  });
-});