|
9 | 9 |
|
10 | 10 | const assert = require('assert').strict; |
11 | 11 | const common = require('../../common'); |
| 12 | +const padManager = require('../../../../node/db/PadManager'); |
12 | 13 |
|
13 | 14 | let agent; |
14 | 15 | const apiKey = common.apiKey; |
@@ -547,6 +548,78 @@ describe(__filename, function () { |
547 | 548 | assert.equal(res.body.code, 0); |
548 | 549 | }); |
549 | 550 | }); |
| 551 | + |
| 552 | + // Regression test for https://github.com/ether/etherpad-lite/issues/5296 |
| 553 | + it('source and destination attribute pools are independent', async function () { |
| 554 | + // Strategy for this test: |
| 555 | + // 1. Create a new pad without bold or italic text |
| 556 | + // 2. Use copyPadWithoutHistory to copy the pad. |
| 557 | + // 3. Add some bold text (but no italic text!) to the source pad. This should add a bold |
| 558 | + // attribute to the source pad's pool but not to the destination pad's pool. |
| 559 | + // 4. Add some italic text (but no bold text!) to the destination pad. This should add an |
| 560 | + // italic attribute to the destination pad's pool with the same number as the newly added |
| 561 | + // bold attribute in the source pad's pool. |
| 562 | + // 5. Add some more text (bold or plain) to the source pad. This will save the source pad to |
| 563 | + // the database after the destination pad has had an opportunity to corrupt the source |
| 564 | + // pad. |
| 565 | + // 6. Export the source and destination pads. Make sure that <em> doesn't appear in the |
| 566 | + // source pad's HTML, and that <strong> doesn't appear int he destination pad's HTML. |
| 567 | + // 7. Force the server to re-init the pads from the database. |
| 568 | + // 8. Repeat step 6. |
| 569 | + // If <em> appears in the source pad, or <strong> appears in the destination pad, then shared |
| 570 | + // state between the two attribute pools caused corruption. |
| 571 | + |
| 572 | + const getHtml = async (padId) => { |
| 573 | + const res = await agent.get(`${endPoint('getHTML')}&padID=${padId}`) |
| 574 | + .expect(200) |
| 575 | + .expect('Content-Type', /json/); |
| 576 | + assert.equal(res.body.code, 0); |
| 577 | + return res.body.data.html; |
| 578 | + }; |
| 579 | + |
| 580 | + const setBody = async (padId, bodyHtml) => { |
| 581 | + await agent.post(endPoint('setHTML')) |
| 582 | + .send({padID: padId, html: `<!DOCTYPE HTML><html><body>${bodyHtml}</body></html>`}) |
| 583 | + .expect(200) |
| 584 | + .expect('Content-Type', /json/) |
| 585 | + .expect((res) => assert.equal(res.body.code, 0)); |
| 586 | + }; |
| 587 | + |
| 588 | + const origHtml = await getHtml(sourcePadId); |
| 589 | + assert.doesNotMatch(origHtml, /<strong>/); |
| 590 | + assert.doesNotMatch(origHtml, /<em>/); |
| 591 | + await agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` + |
| 592 | + `&destinationID=${newPad}&force=false`) |
| 593 | + .expect(200) |
| 594 | + .expect('Content-Type', /json/) |
| 595 | + .expect((res) => assert.equal(res.body.code, 0)); |
| 596 | + |
| 597 | + const newBodySrc = '<strong>bold</strong>'; |
| 598 | + const newBodyDst = '<em>italic</em>'; |
| 599 | + await setBody(sourcePadId, newBodySrc); |
| 600 | + await setBody(newPad, newBodyDst); |
| 601 | + await setBody(sourcePadId, `${newBodySrc} foo`); |
| 602 | + |
| 603 | + let [srcHtml, dstHtml] = await Promise.all([getHtml(sourcePadId), getHtml(newPad)]); |
| 604 | + assert.match(srcHtml, new RegExp(newBodySrc)); |
| 605 | + assert.match(dstHtml, new RegExp(newBodyDst)); |
| 606 | + |
| 607 | + // Force the server to re-read the pads from the database. This rebuilds the attribute pool |
| 608 | + // objects from scratch, ensuring that an internally inconsistent attribute pool object did |
| 609 | + // not cause the above tests to accidentally pass. |
| 610 | + const reInitPad = async (padId) => { |
| 611 | + const pad = await padManager.getPad(padId); |
| 612 | + await pad.init(); |
| 613 | + }; |
| 614 | + await Promise.all([ |
| 615 | + reInitPad(sourcePadId), |
| 616 | + reInitPad(newPad), |
| 617 | + ]); |
| 618 | + |
| 619 | + [srcHtml, dstHtml] = await Promise.all([getHtml(sourcePadId), getHtml(newPad)]); |
| 620 | + assert.match(srcHtml, new RegExp(newBodySrc)); |
| 621 | + assert.match(dstHtml, new RegExp(newBodyDst)); |
| 622 | + }); |
550 | 623 | }); |
551 | 624 | }); |
552 | 625 |
|
|
0 commit comments