|
18 | 18 | * |
19 | 19 | */ |
20 | 20 |
|
21 | | -/*global describe, it, expect, beforeAll, afterAll, awaitsFor */ |
| 21 | +/*global describe, it, expect, beforeAll, afterAll, awaitsFor, spyOn */ |
22 | 22 |
|
23 | 23 | define(function (require, exports, module) { |
24 | 24 |
|
@@ -546,6 +546,185 @@ define(function (require, exports, module) { |
546 | 546 | }); |
547 | 547 | }); |
548 | 548 |
|
| 549 | + describe("Context menu commands", function () { |
| 550 | + let CommandManager; |
| 551 | + |
| 552 | + function getActiveTerminal() { |
| 553 | + const mod = testWindow.brackets.getModule( |
| 554 | + "extensionsIntegrated/Terminal/main" |
| 555 | + ); |
| 556 | + return mod._getActiveTerminal(); |
| 557 | + } |
| 558 | + |
| 559 | + /** |
| 560 | + * Check whether a marker string appears anywhere |
| 561 | + * in the active terminal's buffer. |
| 562 | + */ |
| 563 | + function bufferContains(marker) { |
| 564 | + const active = getActiveTerminal(); |
| 565 | + if (!active) { |
| 566 | + return false; |
| 567 | + } |
| 568 | + const buffer = active.terminal.buffer.active; |
| 569 | + for (let i = 0; i < buffer.length; i++) { |
| 570 | + const line = buffer.getLine(i); |
| 571 | + if (line && line.translateToString() |
| 572 | + .indexOf(marker) !== -1) { |
| 573 | + return true; |
| 574 | + } |
| 575 | + } |
| 576 | + return false; |
| 577 | + } |
| 578 | + |
| 579 | + beforeAll(function () { |
| 580 | + CommandManager = |
| 581 | + testWindow.brackets.test.CommandManager; |
| 582 | + }); |
| 583 | + |
| 584 | + it("should clear the terminal screen", |
| 585 | + async function () { |
| 586 | + await openTerminal(); |
| 587 | + await waitForShellReady(); |
| 588 | + |
| 589 | + // Write some output so the terminal has content |
| 590 | + await writeToTerminal("echo cleartest\r"); |
| 591 | + await awaitsFor(function () { |
| 592 | + return bufferContains("cleartest"); |
| 593 | + }, "echo output to appear", 10000); |
| 594 | + |
| 595 | + // Execute the clear command |
| 596 | + await CommandManager.execute("terminal.clear"); |
| 597 | + |
| 598 | + // After clear, the marker should no longer be |
| 599 | + // in the buffer (xterm.clear() wipes scrollback). |
| 600 | + await awaitsFor(function () { |
| 601 | + return !bufferContains("cleartest"); |
| 602 | + }, "terminal to be cleared", 5000); |
| 603 | + |
| 604 | + // Clean up |
| 605 | + clickPanelCloseButton(); |
| 606 | + await awaitsFor(function () { |
| 607 | + return !testWindow.$("#terminal-panel") |
| 608 | + .is(":visible"); |
| 609 | + }, "terminal panel to close", 5000); |
| 610 | + }); |
| 611 | + |
| 612 | + it("should copy selected terminal text to clipboard", |
| 613 | + async function () { |
| 614 | + await openTerminal(); |
| 615 | + await waitForShellReady(); |
| 616 | + |
| 617 | + // Write a unique marker string |
| 618 | + await writeToTerminal("echo COPYMARKER123\r"); |
| 619 | + await awaitsFor(function () { |
| 620 | + return bufferContains("COPYMARKER123"); |
| 621 | + }, "echo output to appear", 10000); |
| 622 | + |
| 623 | + // Select all text in xterm |
| 624 | + const active = getActiveTerminal(); |
| 625 | + active.terminal.selectAll(); |
| 626 | + expect(active.terminal.hasSelection()) |
| 627 | + .toBeTrue(); |
| 628 | + const selection = active.terminal.getSelection(); |
| 629 | + expect(selection).toContain("COPYMARKER123"); |
| 630 | + |
| 631 | + // The copy command writes to the system clipboard |
| 632 | + // via navigator.clipboard.writeText(). In the test |
| 633 | + // iframe clipboard writes may be denied (no focus), |
| 634 | + // so we verify the command reads the right text by |
| 635 | + // spying on writeText. |
| 636 | + const clipboard = testWindow.navigator.clipboard; |
| 637 | + let copiedText = null; |
| 638 | + spyOn(clipboard, "writeText").and.callFake( |
| 639 | + function (text) { |
| 640 | + copiedText = text; |
| 641 | + return testWindow.Promise.resolve(); |
| 642 | + } |
| 643 | + ); |
| 644 | + await CommandManager.execute("terminal.copy"); |
| 645 | + expect(copiedText).toContain("COPYMARKER123"); |
| 646 | + |
| 647 | + // Clean up |
| 648 | + clickPanelCloseButton(); |
| 649 | + await awaitsFor(function () { |
| 650 | + return !testWindow.$("#terminal-panel") |
| 651 | + .is(":visible"); |
| 652 | + }, "terminal panel to close", 5000); |
| 653 | + }); |
| 654 | + |
| 655 | + it("should paste clipboard text into terminal", |
| 656 | + async function () { |
| 657 | + await openTerminal(); |
| 658 | + await waitForShellReady(); |
| 659 | + |
| 660 | + // Mock clipboard.readText to return a known string, |
| 661 | + // since the test iframe may not have clipboard |
| 662 | + // permission (no window focus). |
| 663 | + const pasteText = "PASTEMARKER456"; |
| 664 | + const clipboard = testWindow.navigator.clipboard; |
| 665 | + spyOn(clipboard, "readText").and.returnValue( |
| 666 | + testWindow.Promise.resolve(pasteText) |
| 667 | + ); |
| 668 | + |
| 669 | + // Execute the paste command |
| 670 | + await CommandManager.execute("terminal.paste"); |
| 671 | + |
| 672 | + // The pasted text should appear in the terminal |
| 673 | + // buffer (written to PTY input → echoed back). |
| 674 | + await awaitsFor(function () { |
| 675 | + return bufferContains(pasteText); |
| 676 | + }, "pasted text to appear in terminal", 10000); |
| 677 | + |
| 678 | + // Clean up: press Enter then close |
| 679 | + await writeToTerminal("\r"); |
| 680 | + clickPanelCloseButton(); |
| 681 | + await awaitsFor(function () { |
| 682 | + return !testWindow.$("#terminal-panel") |
| 683 | + .is(":visible"); |
| 684 | + }, "terminal panel to close", 5000); |
| 685 | + }); |
| 686 | + |
| 687 | + it("should disable copy when there is no selection", |
| 688 | + async function () { |
| 689 | + await openTerminal(); |
| 690 | + await waitForShellReady(); |
| 691 | + |
| 692 | + const active = getActiveTerminal(); |
| 693 | + // Ensure no selection |
| 694 | + active.terminal.clearSelection(); |
| 695 | + expect(active.terminal.hasSelection()) |
| 696 | + .toBeFalse(); |
| 697 | + |
| 698 | + // Open context menu to trigger |
| 699 | + // beforeContextMenuOpen event |
| 700 | + const Menus = testWindow.brackets.test.Menus; |
| 701 | + const ctxMenu = Menus.getContextMenu( |
| 702 | + "terminal-context-menu" |
| 703 | + ); |
| 704 | + ctxMenu.open({pageX: 100, pageY: 100}); |
| 705 | + |
| 706 | + const copyCmd = |
| 707 | + CommandManager.get("terminal.copy"); |
| 708 | + expect(copyCmd.getEnabled()).toBeFalse(); |
| 709 | + |
| 710 | + // Close menu |
| 711 | + ctxMenu.close(); |
| 712 | + |
| 713 | + // Now select text and re-open |
| 714 | + active.terminal.selectAll(); |
| 715 | + ctxMenu.open({pageX: 100, pageY: 100}); |
| 716 | + expect(copyCmd.getEnabled()).toBeTrue(); |
| 717 | + ctxMenu.close(); |
| 718 | + |
| 719 | + // Clean up |
| 720 | + clickPanelCloseButton(); |
| 721 | + await awaitsFor(function () { |
| 722 | + return !testWindow.$("#terminal-panel") |
| 723 | + .is(":visible"); |
| 724 | + }, "terminal panel to close", 5000); |
| 725 | + }); |
| 726 | + }); |
| 727 | + |
549 | 728 | describe("Programmatic hide vs user close", function () { |
550 | 729 | it("should keep terminals alive after panel.hide()", |
551 | 730 | async function () { |
|
0 commit comments