|
|
@@ -0,0 +1,156 @@
|
|
|
+const { expect } = require('chai')
|
|
|
+const SandboxedModule = require('sandboxed-module')
|
|
|
+const MODULE_PATH = require('path').join(
|
|
|
+ __dirname,
|
|
|
+ '../../../../app/src/Features/Helpers/SafeHTMLSubstitution.js'
|
|
|
+)
|
|
|
+
|
|
|
+describe('SafeHTMLSubstitution', function() {
|
|
|
+ let SafeHTMLSubstitution
|
|
|
+ before(function() {
|
|
|
+ SafeHTMLSubstitution = SandboxedModule.require(MODULE_PATH)
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('SPLIT_REGEX', function() {
|
|
|
+ const CASES = {
|
|
|
+ 'PRE<0>INNER</0>POST': ['PRE', '0', 'INNER', 'POST'],
|
|
|
+ '<0>INNER</0>': ['', '0', 'INNER', ''],
|
|
|
+ '<0></0>': ['', '0', '', ''],
|
|
|
+ '<0>INNER</0><0>INNER2</0>': ['', '0', 'INNER', '', '0', 'INNER2', ''],
|
|
|
+ '<0><1>INNER</1></0>': ['', '0', '<1>INNER</1>', ''],
|
|
|
+ 'PLAIN TEXT': ['PLAIN TEXT']
|
|
|
+ }
|
|
|
+ Object.entries(CASES).forEach(([input, output]) => {
|
|
|
+ it(`should parse "${input}" as expected`, function() {
|
|
|
+ expect(input.split(SafeHTMLSubstitution.SPLIT_REGEX)).to.deep.equal(
|
|
|
+ output
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('render', function() {
|
|
|
+ describe('substitution', function() {
|
|
|
+ it('should substitute a single component', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>good</0>', [{ name: 'b' }])
|
|
|
+ ).to.equal('<b>good</b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should substitute a single component as string', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('<0>good</0>', ['b'])).to.equal(
|
|
|
+ '<b>good</b>'
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should substitute a single component twice', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>one</0><0>two</0>', [{ name: 'b' }])
|
|
|
+ ).to.equal('<b>one</b><b>two</b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should substitute two components', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>one</0><1>two</1>', [
|
|
|
+ { name: 'b' },
|
|
|
+ { name: 'i' }
|
|
|
+ ])
|
|
|
+ ).to.equal('<b>one</b><i>two</i>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should substitute a single component with a class', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>text</0>', [
|
|
|
+ {
|
|
|
+ name: 'b',
|
|
|
+ attrs: {
|
|
|
+ class: 'magic'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ])
|
|
|
+ ).to.equal('<b class="magic">text</b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should substitute two nested components', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0><1>nested</1></0>', [
|
|
|
+ { name: 'b' },
|
|
|
+ { name: 'i' }
|
|
|
+ ])
|
|
|
+ ).to.equal('<b><i>nested</i></b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should handle links', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>Go to Login</0>', [
|
|
|
+ { name: 'a', attrs: { href: 'https://www.overleaf.com/login' } }
|
|
|
+ ])
|
|
|
+ ).to.equal('<a href="https://www.overleaf.com/login">Go to Login</a>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should not complain about too many components', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>good</0>', [
|
|
|
+ { name: 'b' },
|
|
|
+ { name: 'i' },
|
|
|
+ { name: 'u' }
|
|
|
+ ])
|
|
|
+ ).to.equal('<b>good</b>')
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('pug.escape', function() {
|
|
|
+ it('should handle plain text', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('plain text')).to.equal('plain text')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should keep a simple string delimiter', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render("'")).to.equal(`'`)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape double quotes', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('"')).to.equal(`"`)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape &', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('&')).to.equal(`&`)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape <', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('<')).to.equal(`<`)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape >', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('>')).to.equal(`>`)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape html', function() {
|
|
|
+ expect(SafeHTMLSubstitution.render('<b>bad</b>')).to.equal(
|
|
|
+ '<b>bad</b>'
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('escape around substitutions', function() {
|
|
|
+ it('should escape text inside a component', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0><i>inner</i></0>', [{ name: 'b' }])
|
|
|
+ ).to.equal('<b><i>inner</i></b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape text in front of a component', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<i>PRE</i><0>inner</0>', [{ name: 'b' }])
|
|
|
+ ).to.equal('<i>PRE</i><b>inner</b>')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should escape text after of a component', function() {
|
|
|
+ expect(
|
|
|
+ SafeHTMLSubstitution.render('<0>inner</0><i>POST</i>', [
|
|
|
+ { name: 'b' }
|
|
|
+ ])
|
|
|
+ ).to.equal('<b>inner</b><i>POST</i>')
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|