]> git.proxmox.com Git - mirror_xterm.js.git/blobdiff - src/Linkifier.test.ts
Create `terminal.buffer` convenience attribute
[mirror_xterm.js.git] / src / Linkifier.test.ts
index 16e388faff80bb1afec4f4caef453353e93d8426..132ce5f06e5258780ad48ee18c83f30aff2b0fd4 100644 (file)
@@ -8,15 +8,16 @@ import { Linkifier } from './Linkifier';
 import { LinkMatcher } from './Types';
 
 class TestLinkifier extends Linkifier {
-  constructor(document: Document, rows: HTMLElement[]) {
+  constructor() {
     Linkifier.TIME_BEFORE_LINKIFY = 0;
-    super(document, rows);
+    super();
   }
 
   public get linkMatchers(): LinkMatcher[] { return this._linkMatchers; }
 }
 
 describe('Linkifier', () => {
+  let dom: jsdom.JSDOM;
   let window: Window;
   let document: Document;
 
@@ -24,16 +25,11 @@ describe('Linkifier', () => {
   let rows: HTMLElement[];
   let linkifier: TestLinkifier;
 
-  beforeEach(done => {
-    rows = [];
-    jsdom.env('', (err, w) => {
-      window = w;
-      document = window.document;
-      linkifier = new TestLinkifier(document, rows);
-      container = document.createElement('div');
-      document.body.appendChild(container);
-      done();
-    });
+  beforeEach(() => {
+    dom = new jsdom.JSDOM('');
+    window = dom.window;
+    document = window.document;
+    linkifier = new TestLinkifier();
   });
 
   function addRow(html: string) {
@@ -43,119 +39,154 @@ describe('Linkifier', () => {
     rows.push(element);
   }
 
-  function clickElement(element: Node) {
-    const event = document.createEvent('MouseEvent');
-    event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
-    element.dispatchEvent(event);
-  }
-
-  describe('http links', () => {
-    function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
-      addRow(uri);
-      linkifier.linkifyRow(0);
-      setTimeout(() => {
-        assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
-        assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
+  describe('before attachToDom', () => {
+    it('should allow link matcher registration', done => {
+      assert.doesNotThrow(() => {
+        const linkMatcherId = linkifier.registerLinkMatcher(/foo/, () => {});
+        assert.isTrue(linkifier.deregisterLinkMatcher(linkMatcherId));
         done();
-      }, 0);
-    }
-    it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
+      });
+    });
   });
 
-  describe('link matcher', () => {
-    function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone) {
-      addRow(rowText);
-      linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
-      linkifier.linkifyRow(0);
-      // Allow linkify to happen
-      setTimeout(() => {
-        assert.equal(rows[0].innerHTML, expectedHtml);
-        done();
-      }, 0);
-    }
-    it('should match a single link', done => {
-      assertLinkifiesRow('foo', /foo/, '<a>foo</a>', done);
-    });
-    it('should match a single link at the start of a text node', done => {
-      assertLinkifiesRow('foo bar', /foo/, '<a>foo</a> bar', done);
-    });
-    it('should match a single link in the middle of a text node', done => {
-      assertLinkifiesRow('foo bar baz', /bar/, 'foo <a>bar</a> baz', done);
-    });
-    it('should match a single link at the end of a text node', done => {
-      assertLinkifiesRow('foo bar', /bar/, 'foo <a>bar</a>', done);
-    });
-    it('should match a link after a link at the start of a text node', done => {
-      assertLinkifiesRow('foo bar', /foo|bar/, '<a>foo</a> <a>bar</a>', done);
-    });
-    it('should match a link after a link in the middle of a text node', done => {
-      assertLinkifiesRow('foo bar baz', /bar|baz/, 'foo <a>bar</a> <a>baz</a>', done);
-    });
-    it('should match a link immediately after a link at the end of a text node', done => {
-      assertLinkifiesRow('<span>foo bar</span>baz', /bar|baz/, '<span>foo <a>bar</a></span><a>baz</a>', done);
+  describe('after attachToDom', () => {
+    beforeEach(() => {
+      rows = [];
+      linkifier.attachToDom(document, rows);
+      container = document.createElement('div');
+      document.body.appendChild(container);
     });
-  });
 
-  describe('validationCallback', () => {
-    it('should enable link if true', done => {
-      addRow('test');
-      linkifier.registerLinkMatcher(/test/, () => done(), {
-        validationCallback: (url, cb) => {
-          cb(true);
+    function clickElement(element: Node) {
+      const event = document.createEvent('MouseEvent');
+      event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+      element.dispatchEvent(event);
+    }
+
+    function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
+        addRow(uri);
+        linkifier.linkifyRow(0);
+        setTimeout(() => {
           assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
-          setTimeout(() => clickElement(rows[0].firstChild), 0);
-        }
-      });
-      linkifier.linkifyRow(0);
-    });
+          assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
+          done();
+        }, 0);
+    }
 
-    it('should disable link if false', done => {
-      addRow('test');
-      linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
-        validationCallback: (url, cb) => {
-          cb(false);
+    describe('http links', () => {
+      function assertLinkifiesEntireRow(uri: string, done: MochaDone) {
+        addRow(uri);
+        linkifier.linkifyRow(0);
+        setTimeout(() => {
           assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
-          setTimeout(() => clickElement(rows[0].firstChild), 0);
-        }
+          assert.equal((<HTMLElement>rows[0].firstChild).textContent, uri);
+          done();
+        }, 0);
+      }
+      it('should allow ~ character in URI path', done => assertLinkifiesEntireRow('http://foo.com/a~b#c~d?e~f', done));
+    });
+
+    describe('link matcher', () => {
+      function assertLinkifiesRow(rowText: string, linkMatcherRegex: RegExp, expectedHtml: string, done: MochaDone) {
+        addRow(rowText);
+        linkifier.registerLinkMatcher(linkMatcherRegex, () => {});
+        linkifier.linkifyRow(0);
+        // Allow linkify to happen
+        setTimeout(() => {
+          assert.equal(rows[0].innerHTML, expectedHtml);
+          done();
+        }, 0);
+      }
+      it('should match a single link', done => {
+        assertLinkifiesRow('foo', /foo/, '<a>foo</a>', done);
+      });
+      it('should match a single link at the start of a text node', done => {
+        assertLinkifiesRow('foo bar', /foo/, '<a>foo</a> bar', done);
+      });
+      it('should match a single link in the middle of a text node', done => {
+        assertLinkifiesRow('foo bar baz', /bar/, 'foo <a>bar</a> baz', done);
+      });
+      it('should match a single link at the end of a text node', done => {
+        assertLinkifiesRow('foo bar', /bar/, 'foo <a>bar</a>', done);
+      });
+      it('should match a link after a link at the start of a text node', done => {
+        assertLinkifiesRow('foo bar', /foo|bar/, '<a>foo</a> <a>bar</a>', done);
+      });
+      it('should match a link after a link in the middle of a text node', done => {
+        assertLinkifiesRow('foo bar baz', /bar|baz/, 'foo <a>bar</a> <a>baz</a>', done);
+      });
+      it('should match a link immediately after a link at the end of a text node', done => {
+        assertLinkifiesRow('<span>foo bar</span>baz', /bar|baz/, '<span>foo <a>bar</a></span><a>baz</a>', done);
+      });
+      it('should not duplicate text after a unicode character (wrapped in a span)', done => {
+        // This is a regression test for an issue that came about when using
+        // an oh-my-zsh theme that added the large blue diamond unicode
+        // character (U+1F537) which caused the path to be duplicated. See #642.
+        assertLinkifiesRow('echo \'<span class="xterm-normal-char">🔷</span>foo\'', /foo/, 'echo \'<span class="xterm-normal-char">🔷</span><a>foo</a>\'', done);
       });
-      linkifier.linkifyRow(0);
-      // Allow time for the click to be performed
-      setTimeout(() => done(), 10);
     });
 
-    it('should trigger for multiple link matches on one row', done => {
-      addRow('test test');
-      let count = 0;
-      linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
-        validationCallback: (url, cb) => {
-          count += 1;
-          if (count === 2) {
-            done();
+    describe('validationCallback', () => {
+      it('should enable link if true', done => {
+        addRow('test');
+        linkifier.registerLinkMatcher(/test/, () => done(), {
+          validationCallback: (url, element, cb) => {
+            cb(true);
+            assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
+            setTimeout(() => clickElement(rows[0].firstChild), 0);
           }
-          cb(false);
-        }
+        });
+        linkifier.linkifyRow(0);
       });
-      linkifier.linkifyRow(0);
-    });
-  });
 
-  describe('priority', () => {
-    it('should order the list from highest priority to lowest #1', () => {
-      const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
-      const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
-      assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
-    });
+      it('should disable link if false', done => {
+        addRow('test');
+        linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
+          validationCallback: (url, element, cb) => {
+            cb(false);
+            assert.equal((<HTMLElement>rows[0].firstChild).tagName, 'A');
+            setTimeout(() => clickElement(rows[0].firstChild), 0);
+          }
+        });
+        linkifier.linkifyRow(0);
+        // Allow time for the click to be performed
+        setTimeout(() => done(), 10);
+      });
 
-    it('should order the list from highest priority to lowest #2', () => {
-      const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
-      const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
-      assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
+      it('should trigger for multiple link matches on one row', done => {
+        addRow('test test');
+        let count = 0;
+        linkifier.registerLinkMatcher(/test/, () => assert.fail(), {
+          validationCallback: (url, element, cb) => {
+            count += 1;
+            if (count === 2) {
+              done();
+            }
+            cb(false);
+          }
+        });
+        linkifier.linkifyRow(0);
+      });
     });
 
-    it('should order items of equal priority in the order they are added', () => {
-      const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
-      const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
-      assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
+    describe('priority', () => {
+      it('should order the list from highest priority to lowest #1', () => {
+        const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 1 });
+        const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: -1 });
+        assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [aId, 0, bId]);
+      });
+
+      it('should order the list from highest priority to lowest #2', () => {
+        const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: -1 });
+        const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 1 });
+        assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [bId, 0, aId]);
+      });
+
+      it('should order items of equal priority in the order they are added', () => {
+        const aId = linkifier.registerLinkMatcher(/a/, () => {}, { priority: 0 });
+        const bId = linkifier.registerLinkMatcher(/b/, () => {}, { priority: 0 });
+        assert.deepEqual(linkifier.linkMatchers.map(lm => lm.id), [0, aId, bId]);
+      });
     });
   });
 });