diff --git a/cspell.json b/cspell.json index e5725632cb8..da0323825c7 100644 --- a/cspell.json +++ b/cspell.json @@ -6,6 +6,7 @@ "gapi", "googleusercontent", "Hira", + "Interconvert", "mbit", "smalrubot" ], diff --git a/src/lib/ruby-generator/index.js b/src/lib/ruby-generator/index.js index 36487ac5fe6..d32976dff6e 100644 --- a/src/lib/ruby-generator/index.js +++ b/src/lib/ruby-generator/index.js @@ -333,7 +333,14 @@ RubyGenerator.scrubNakedValue = function (line) { RubyGenerator.escapeChars_ = { '"': '\\"', - '\\': '\\\\' + '\\': '\\\\', + '\n': '\\n', + '\t': '\\t', + '\r': '\\r', + '\b': '\\b', + '\f': '\\f', + '\v': '\\v', + '\0': '\\0' }; RubyGenerator.quote_ = function (string) { diff --git a/test/integration/ruby-tab/operators.test.js b/test/integration/ruby-tab/operators.test.js index a15690eb44e..d508d7d3859 100644 --- a/test/integration/ruby-tab/operators.test.js +++ b/test/integration/ruby-tab/operators.test.js @@ -1,12 +1,9 @@ import dedent from 'dedent'; import SeleniumHelper from '../../helpers/selenium-helper'; import RubyHelper from '../../helpers/ruby-helper'; -import {EDIT_MENU_XPATH} from '../../helpers/menu-xpaths'; const seleniumHelper = new SeleniumHelper(); const { - clickText, - clickXpath, getDriver, loadUri, urlFor @@ -14,8 +11,6 @@ const { const rubyHelper = new RubyHelper(seleniumHelper); const { - fillInRubyProgram, - currentRubyProgram, expectInterconvertBetweenCodeAndRuby } = rubyHelper; @@ -98,28 +93,4 @@ describe('Ruby Tab: Operators category blocks', () => { `; await expectInterconvertBetweenCodeAndRuby(code); }); - - test('Ruby -> Code -> Ruby (escape characters) ', async () => { - await loadUri(urlFor('/')); - - const beforeRuby = dedent` - "\\\\" + "\\\\" - - "\\n" + "\\n" - `; - - const afterRuby = dedent` - "\\" + "\\" - - "\n" + "\n" - `; - - await clickText('Ruby', '*[@role="tab"]'); - await fillInRubyProgram(beforeRuby); - await clickText('Code', '*[@role="tab"]'); - await clickXpath(EDIT_MENU_XPATH); - await clickText('Generate Ruby from Code'); - await clickText('Ruby', '*[@role="tab"]'); - expect(await currentRubyProgram()).toEqual(`${afterRuby}\n`); - }); }); diff --git a/test/unit/lib/ruby-generator/index.test.js b/test/unit/lib/ruby-generator/index.test.js new file mode 100644 index 00000000000..4e2ca9ea1fb --- /dev/null +++ b/test/unit/lib/ruby-generator/index.test.js @@ -0,0 +1,50 @@ +import RubyGenerator from '../../../../src/lib/ruby-generator'; + +describe('RubyGenerator', () => { + describe('quote_', () => { + test('should escape double quotes', () => { + const result = RubyGenerator.quote_('"'); + expect(result).toBe('"\\\""'); + }); + + test('should escape backslashes', () => { + const result = RubyGenerator.quote_('\\'); + expect(result).toBe('"\\\\"'); + }); + + test('should escape newline characters', () => { + const result = RubyGenerator.quote_('\n'); + expect(result).toBe('"\\n"'); + }); + + test('should escape tab characters', () => { + const result = RubyGenerator.quote_('\t'); + expect(result).toBe('"\\t"'); + }); + + test('should escape carriage return characters', () => { + const result = RubyGenerator.quote_('\r'); + expect(result).toBe('"\\r"'); + }); + + test('should escape backspace characters', () => { + const result = RubyGenerator.quote_('\b'); + expect(result).toBe('"\\b"'); + }); + + test('should escape form feed characters', () => { + const result = RubyGenerator.quote_('\f'); + expect(result).toBe('"\\f"'); + }); + + test('should escape vertical tab characters', () => { + const result = RubyGenerator.quote_('\v'); + expect(result).toBe('"\\v"'); + }); + + test('should escape null characters', () => { + const result = RubyGenerator.quote_('\0'); + expect(result).toBe('"\\0"'); + }); + }); +}); diff --git a/test/unit/lib/ruby-generator/looks.test.js b/test/unit/lib/ruby-generator/looks.test.js index b6b239f1c4e..22ef0785893 100644 --- a/test/unit/lib/ruby-generator/looks.test.js +++ b/test/unit/lib/ruby-generator/looks.test.js @@ -82,6 +82,30 @@ describe('RubyGenerator/Looks', () => { const expected = 'say("Hello!")\n'; expect(RubyGenerator.looks_say(block)).toEqual(expected); }); + + test('print with newline character', () => { + const block = { + id: 'block-id', + opcode: 'looks_say', + inputs: { MESSAGE: {} } + }; + RubyGenerator.cache_.comments['block-id'] = { text: '@ruby:method:print' }; + RubyGenerator.valueToCode = jest.fn().mockReturnValue('"Hello, Ruby.\\n"'); + const expected = 'print("Hello, Ruby.\\n")\n'; + expect(RubyGenerator.looks_say(block)).toEqual(expected); + }); + + test('puts with tab character', () => { + const block = { + id: 'block-id', + opcode: 'looks_say', + inputs: { MESSAGE: {} } + }; + RubyGenerator.cache_.comments['block-id'] = { text: '@ruby:method:puts' }; + RubyGenerator.valueToCode = jest.fn().mockReturnValue('"Hello\\tRuby"'); + const expected = 'puts("Hello\\tRuby")\n'; + expect(RubyGenerator.looks_say(block)).toEqual(expected); + }); }); describe('scrub_ (meta-comment filtering)', () => {