utils.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global navigator */
  6. import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml';
  7. import { getData as getModelData, stringify as stringifyModel } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
  8. import { stringify, getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
  9. /**
  10. * Checks whether for a given editor instance pasting specific content (input) gives expected result (output).
  11. *
  12. * @param {module:core/editor/editor~Editor} editor
  13. * @param {String} input Data to paste.
  14. * @param {String} expectedModel Expected model.
  15. * @param {String} [expectedView=null] Expected view.
  16. */
  17. export function expectPaste( editor, input, expectedModel, expectedView = null ) {
  18. pasteHtml( editor, input );
  19. expect( getModelData( editor.model ) ).to.equal( expectedModel );
  20. if ( expectedView ) {
  21. expect( getViewData( editor.editing.view ) ).to.equal( expectedView );
  22. }
  23. }
  24. /**
  25. * Fires paste event on a given editor instance with a specific HTML data.
  26. *
  27. * @param {module:core/editor/editor~Editor} editor Editor instance on which paste event will be fired.
  28. * @param {String} html The HTML data with which paste event will be fired.
  29. */
  30. export function pasteHtml( editor, html ) {
  31. editor.editing.view.document.fire( 'paste', {
  32. dataTransfer: createDataTransfer( { 'text/html': html } ),
  33. preventDefault() {}
  34. } );
  35. }
  36. /**
  37. * Mocks dataTransfer object which can be used for simulating paste.
  38. *
  39. * @param {Object} data Object containing "mime type - data" pairs.
  40. * @returns {Object} DataTransfer mock object.
  41. */
  42. export function createDataTransfer( data ) {
  43. return {
  44. getData( type ) {
  45. return data[ type ];
  46. }
  47. };
  48. }
  49. /**
  50. * Compares two HTML strings.
  51. *
  52. * This function is designed for comparing normalized data so expected input is preprocessed before comparing:
  53. *
  54. * * Tabs on the lines beginning are removed.
  55. * * Line breaks and empty lines are removed.
  56. *
  57. * The expected input should be prepared in the above in mind which means every element containing text nodes must start
  58. * and end in the same line. So expected input may be formatted like:
  59. *
  60. * <span lang=PL style='mso-ansi-language:PL'> 03<span style='mso-spacerun:yes'> </span><o:p></o:p></span>
  61. *
  62. * but not like:
  63. *
  64. * <span lang=PL style='mso-ansi-language:PL'>
  65. * 03<span style='mso-spacerun:yes'> </span>
  66. * <o:p></o:p>
  67. * </span>
  68. *
  69. * because tabulator preceding `03` text will be treated as formatting character and will be removed.
  70. *
  71. * @param {String} actual
  72. * @param {String} expected
  73. */
  74. export function expectNormalized( actual, expected ) {
  75. const expectedInlined = expected
  76. // Replace tabs on the lines beginning as normalized input files are formatted.
  77. .replace( /^\t*</gm, '<' )
  78. // Replace line breaks (after closing tags) too.
  79. .replace( /[\r\n]/gm, '' );
  80. // We are ok with both spaces and non-breaking spaces in the actual content.
  81. // Replace `&nbsp;` with regular spaces to align with expected content (where regular spaces are only used).
  82. const actualNormalized = stringify( actual ).replace( /\u00A0/g, ' ' );
  83. expect( actualNormalized ).to.equal( normalizeHtml( expectedInlined ) );
  84. }
  85. /**
  86. * Compares two models string representations. This function hooks into {@link module:engine/model/model~Model#insertContent}
  87. * to get the model representarion before it is inserted.
  88. *
  89. * @param {module:core/editor/editor~Editor} editor
  90. * @param {String} input
  91. * @param {String} expected
  92. */
  93. export function expectModel( editor, input, expected ) {
  94. const editorModel = editor.model;
  95. const insertContent = editorModel.insertContent;
  96. let actual = '';
  97. sinon.stub( editorModel, 'insertContent' ).callsFake( ( content, selection ) => {
  98. // Save model string representation now as it may change after `insertContent()` function call
  99. // so accessing it later may not work as it may have empty/changed structure.
  100. actual = stringifyModel( content );
  101. insertContent.call( editorModel, content, selection );
  102. } );
  103. pasteHtml( editor, input );
  104. sinon.restore();
  105. expect( actual.replace( /\u00A0/g, '#' ).replace( /&nbsp;/g, '#' ) )
  106. .to.equal( expected.replace( /\u00A0/g, '#' ).replace( /&nbsp;/g, '#' ) );
  107. }
  108. /**
  109. * Returns the name of the browser in which code is executed based on `window.navigator` object.
  110. *
  111. * @returns {String|null} Lowercase browser name or null if non-standard browser is used.
  112. */
  113. export function getBrowserName() {
  114. const browsers = detectBrowsers( navigator );
  115. const browser = Object.keys( browsers ).filter( browserName => !!browsers[ browserName ] );
  116. return browser.length ? browser[ 0 ] : null;
  117. }
  118. // Checks if current browser is one of the predefined ones (Chrome, Edge, Firefox, IE, Safari).
  119. //
  120. // @param {Navigator} navigator Browser `window.navigator` object on which detection is based.
  121. // @returns {{chrome: Boolean, edge: Boolean, firefox: Boolean, ie: Boolean, safari: Boolean}}
  122. function detectBrowsers( navigator ) {
  123. const agent = navigator.userAgent.toLowerCase();
  124. const edge = agent.match( /edge[ /](\d+.?\d*)/ );
  125. const trident = agent.indexOf( 'trident/' ) > -1;
  126. const ie = !!( edge || trident );
  127. const webkit = !ie && ( agent.indexOf( ' applewebkit/' ) > -1 );
  128. const gecko = navigator.product === 'Gecko' && !webkit && !ie;
  129. const chrome = webkit && agent.indexOf( 'chrome' ) > -1;
  130. return {
  131. chrome,
  132. edge: !!edge,
  133. firefox: gecko,
  134. ie,
  135. safari: webkit && !chrome,
  136. };
  137. }