PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ckeditor4/src/ckeditor-dev/tests/plugins/widget/_helpers/tools.js

https://github.com/CoreMedia/jangaroo-libs
JavaScript | 404 lines | 355 code | 29 blank | 20 comment | 12 complexity | ec83d8d86e43c66106bf37b25f0f72e1 MD5 | raw file
  1. /* exported widgetTestsTools */
  2. var widgetTestsTools = ( function() {
  3. 'use strict';
  4. //
  5. // @param config
  6. // @param config.name
  7. // @param config.widgetName
  8. // @param [config.editorConfig]
  9. // @param [config.extraPlugins]
  10. // @param [config.extraAllowedContent]
  11. // @param [config.onWidgetDefinition]
  12. //
  13. // @param config.initialInstancesNumber
  14. // @param [config.checkData]
  15. // @param [config.assertWidgets]
  16. //
  17. // @param config.newData
  18. // @param config.newWidgetPattern
  19. // @param {Boolean/Function} [config.ignoreStyle=false]
  20. function addTests( tcs, config ) {
  21. var editor,
  22. editorBot,
  23. tcName = config.name,
  24. checkData = config.checkData !== false,
  25. initialData,
  26. editorConfig = {
  27. extraPlugins: config.extraPlugins + ',sourcearea,undo,clipboard,toolbar',
  28. removePlugins: config.removePlugins,
  29. extraAllowedContent: config.extraAllowedContent,
  30. on: {
  31. loaded: function( evt ) {
  32. editor = evt.editor;
  33. initialData = fixHtml( editor.getData(), config.ignoreStyle, editor );
  34. editor.dataProcessor.writer.sortAttributes = true;
  35. },
  36. widgetDefinition: function( evt ) {
  37. if ( evt.data.name == config.widgetName && config.onWidgetDefinition )
  38. config.onWidgetDefinition( evt.data );
  39. }
  40. }
  41. };
  42. if ( config.editorConfig )
  43. CKEDITOR.tools.extend( editorConfig, config.editorConfig, true );
  44. tcs[ 'test ' + tcName + ' - init' ] = function() {
  45. bender.editorBot.create( {
  46. name: 'editor_' + tcName,
  47. creator: config.creator,
  48. config: editorConfig
  49. }, function( bot ) {
  50. editorBot = bot;
  51. editor.focus();
  52. assert.isNotUndefined( editor.widgets.registered[ config.widgetName ], 'widget definition is registered' );
  53. assertWidgets( 'on init' );
  54. } );
  55. };
  56. if ( config.creator != 'inline' ) {
  57. tcs[ 'test ' + tcName + ' - switch modes' ] = function() {
  58. var sourceModeData;
  59. // Wait & ensure async.
  60. wait( function() {
  61. editor.setMode( 'source', function() {
  62. sourceModeData = fixHtml( editor.getData(), config.ignoreStyle, editor );
  63. editor.setMode( 'wysiwyg', function() {
  64. resume( function() {
  65. checkData && assert.areSame( initialData, sourceModeData, 'source mode data' );
  66. assertWidgets( 'after switching modes' );
  67. } );
  68. } );
  69. } );
  70. } );
  71. };
  72. }
  73. tcs[ 'test ' + tcName + ' - paste' ] = function() {
  74. var html = editor.editable().getHtml();
  75. editorBot.setData( '', function() {
  76. editor.once( 'afterPaste', function() {
  77. resume( function() {
  78. assertWidgets( 'after paste' );
  79. } );
  80. } );
  81. wait( function() {
  82. editor.execCommand( 'paste', html );
  83. } );
  84. } );
  85. };
  86. tcs[ 'test ' + tcName + ' - create' ] = function() {
  87. editorBot.setData( '', function() {
  88. var widgetDef = editor.widgets.registered[ config.widgetName ];
  89. editor.once( 'dialogShow', function( evt ) {
  90. var dialog = evt.data;
  91. setTimeout( function() {
  92. for ( var i = 0; i < config.newData.length; ++i )
  93. dialog.setValueOf.apply( dialog, config.newData[ i ] );
  94. dialog.getButton( 'ok' ).click();
  95. }, 50 );
  96. } );
  97. editor.once( 'dialogHide', function() {
  98. resume( function() {
  99. var instances = bender.tools.objToArray( editor.widgets.instances );
  100. assert.areSame( 1, instances.length, 'one instance was created' );
  101. assert.isMatching( config.newWidgetPattern, editor.getData(), 'data' );
  102. } );
  103. } );
  104. // We lose focus somewhere in the previous tests. Regain it, so command's state is refreshed.
  105. editor.focus();
  106. editor.execCommand( widgetDef.name );
  107. wait();
  108. } );
  109. };
  110. function assertWidgets( msg ) {
  111. var instances = bender.tools.objToArray( editor.widgets.instances );
  112. assert.areSame( config.initialInstancesNumber, instances.length, 'instances number ' + msg );
  113. checkData && assert.areSame( initialData, fixHtml( editor.getData(), config.ignoreStyle, editor ), 'data ' + msg );
  114. var editable = editor.editable();
  115. for ( var i = 0; i < instances.length; ++i )
  116. assert.isTrue( editable.contains( instances[ i ].wrapper ), 'editable contains wrapper ' + msg );
  117. config.assertWidgets && config.assertWidgets( editor, msg );
  118. }
  119. }
  120. function classes2Array( classesObj ) {
  121. return CKEDITOR.tools.object.keys( classesObj ).sort();
  122. }
  123. function fixHtml( html, ignoreStyle, editor ) {
  124. // Because IE modify style attribute we should fix it or totally ignore style attribute.
  125. html = html.replace( /style="([^"]*)"/g, function( styleStr ) {
  126. ignoreStyle = typeof ignoreStyle === 'function' ? ignoreStyle( editor ) : ignoreStyle;
  127. // If there are too many problems with styles just ignore them.
  128. if ( ignoreStyle ) {
  129. return '';
  130. }
  131. // If it is only the matter of spacers and semicolons fix attributes.
  132. var style = styleStr.substr( 7, styleStr.length - 8 );
  133. return 'style="' + CKEDITOR.tools.htmlEncodeAttr( CKEDITOR.tools.writeCssText( CKEDITOR.tools.parseCssText( style ) ) ) + '"';
  134. } );
  135. // And some additional cleanup.
  136. html = bender.tools.compatHtml( html, true, true, true, true );
  137. return html;
  138. }
  139. function data2Attr( data ) {
  140. return encodeURIComponent( JSON.stringify( data ) );
  141. }
  142. function getAttrData( widget ) {
  143. return JSON.parse( decodeURIComponent( widget.element.data( 'cke-widget-data' ) ) );
  144. }
  145. // @param {Boolean} [byElement] If true, the passed id has to be widget element's id.
  146. // Important for nested widgets, so parent widget is not mistakenly found.
  147. function getWidgetById( editor, id, byElement ) {
  148. var widget = editor.widgets.getByElement( editor.document.getById( id ) );
  149. if ( widget && byElement )
  150. return widget.element.$.id == id ? widget : null;
  151. return widget;
  152. }
  153. // Retrives widget by its offset among parsed widgets.
  154. //
  155. // @param {CKEDITOR.editor} editor
  156. // @param {Number} offset 0-based widget offset.
  157. // @returns {CKEDITOR.plugins.widget/null} Returns null if widget was not found.
  158. function getWidgetByDOMOffset( editor, offset ) {
  159. var wrapper = editor.editable().find( '.cke_widget_wrapper' ).getItem( offset ),
  160. ret = null;
  161. if ( wrapper )
  162. ret = editor.widgets.getByElement( wrapper );
  163. return ret;
  164. }
  165. // Sets editor content to given element and tests if its downcasted to expected widget.
  166. //
  167. // @param {bender.editorBot} editorBot
  168. // @param {String} id Id of element in document which.
  169. // @param {Number} [expectedInstancesCount=1] Expected widget instances count.
  170. // @param {String} [expectedWidgetName] Expected widget definiton name. In this case
  171. // should have same definition name.
  172. function assertDowncast( editorBot, id, expectedInstancesCount, expectedWidgetName ) {
  173. var data = CKEDITOR.document.getById( id ).getHtml();
  174. if ( typeof expectedInstancesCount == 'undefined' )
  175. expectedInstancesCount = 1;
  176. editorBot.setData( data, function() {
  177. var instancesArray = bender.tools.objToArray( editorBot.editor.widgets.instances );
  178. assert.areEqual( expectedInstancesCount, instancesArray.length, 'Invalid count of created widget instances.' );
  179. assert.areEqual( fixHtml( data ), fixHtml( editorBot.getData() ), 'Editor html after performing downcast is not matching.' );
  180. if ( expectedWidgetName ) {
  181. for ( var i = instancesArray.length - 1; i >= 0; i-- )
  182. assert.areEqual( expectedWidgetName, instancesArray[ i ].name, 'Widget at index ' + i + ' has widget invalid definition name.' );
  183. }
  184. } );
  185. }
  186. // Asserts that dialog appears after executing given command, and that dialog
  187. // fields contains given values.
  188. //
  189. // Uses global functions: wait, resume.
  190. //
  191. // For sample use see: dt/plugins/image2/editing.html
  192. //
  193. // @param {bender.editorBot} editorBot
  194. // @param {String} commandName Name of command which should create dialog.
  195. // @param {String} editorHtml Initial HTML code for editor, should contain code transformable into the widget.
  196. // @param {Number/String} focusedWidgetIndex If String: id of widget which should be focused
  197. // if Number: index of widget which should be focused (0-based), otherwise: no widget will be focused (default)
  198. // @param {Object} expectedValues Expected dialog values object. If null - checking will be skipped.
  199. // @param {String} htmlWithSelectionSource If passed it will use htmlWithSelection() method and
  200. // override content set with editorHtml variable.
  201. // @param {Function} onResume Callback called after checking dialog values, passes
  202. // dialog object as its first argument.
  203. function assertWidgetDialog( editorBot, commandName, editorHtml, focusedWidgetIndex, expectedValues, htmlWithSelectionSource, onResume ) {
  204. var editor = editorBot.editor,
  205. dialogCheckedTab = 'info'; // Tab name in dialog.
  206. // Registering dialogShow listener, which will invoke resume().
  207. editor.once( 'dialogShow', function( evt ) {
  208. var dialog = evt.data;
  209. resume( function() {
  210. // Lets check dialog contents.
  211. try {
  212. expectedValues = expectedValues || {};
  213. for ( var i in expectedValues )
  214. assert.areSame( expectedValues[ i ], dialog.getValueOf( dialogCheckedTab, i ), 'Dialog field "' + i + '" in tab "' + dialogCheckedTab + '" has invalid value.' );
  215. } catch ( e ) {
  216. // Propagate error.
  217. dialog.hide();
  218. throw e;
  219. }
  220. onResume && onResume( dialog );
  221. dialog.hide();
  222. } );
  223. } );
  224. editorBot.setData( editorHtml, function() {
  225. var widget = null;
  226. // If html with seleciton was requested - override.
  227. if ( typeof htmlWithSelectionSource == 'string' )
  228. editorBot.htmlWithSelection( htmlWithSelectionSource );
  229. if ( typeof focusedWidgetIndex == 'number' ) {
  230. // If index of widget to be focused is number.
  231. widget = getWidgetByDOMOffset( editor, focusedWidgetIndex );
  232. assert.isNotNull( widget, 'Could not find widget with index ' + focusedWidgetIndex );
  233. } else if ( typeof focusedWidgetIndex == 'string' ) {
  234. // In this case id was given.
  235. widget = getWidgetById( editor, focusedWidgetIndex );
  236. assert.isNotNull( widget, 'Could not find widget with id ' + focusedWidgetIndex );
  237. }
  238. if ( widget )
  239. widget.focus();
  240. assert.isTrue( true );
  241. editor.execCommand( commandName );
  242. wait();
  243. } );
  244. }
  245. function assertWidget( config ) {
  246. var editor = config.bot.editor,
  247. // Widget instances as array.
  248. instancesArray = null;
  249. config.bot.setData( config.html, function() {
  250. var widget = null,
  251. pickWidget = function() {
  252. var ret = null;
  253. if ( config.widgetId )
  254. ret = widgetTestsTools.getWidgetById( editor, config.widgetId );
  255. else if ( typeof config.widgetOffset !== 'undefined' )
  256. ret = widgetTestsTools.getWidgetByDOMOffset( editor, config.widgetOffset );
  257. return ret;
  258. };
  259. instancesArray = bender.tools.objToArray( editor.widgets.instances );
  260. // If expected widgets count was specified, check if it's the same.
  261. typeof config.count !== 'undefined' && assert.areSame( Number( config.count ), instancesArray.length, 'Invalid count of widgets found.' );
  262. // There is a case that config.count will be 0, so we dont want to have any widgets found.
  263. if ( typeof config.count !== 'number' || config.count > 0 ) {
  264. widget = pickWidget();
  265. // Assert widget once it's created.
  266. config.assertCreated && config.assertCreated( widget );
  267. // Assert widget name.
  268. assert.areSame( config.nameCreated, widget.name, 'Initial widget name must match.' );
  269. // Set new widget data. This may destroy the widget.
  270. config.data && widget.setData( config.data );
  271. // Retrieve the widget once again as it may have been destroyed.
  272. widget = pickWidget();
  273. // Assert widget as new data is set.
  274. config.assertNewData && config.assertNewData( widget );
  275. // Assert widget name as new data is set.
  276. config.nameNewData && assert.areSame( config.nameNewData, widget.name, 'Widget name must match after data is set.' );
  277. // Finally destroy the widget.
  278. editor.widgets.destroy( widget );
  279. }
  280. config.callback && config.callback( config );
  281. } );
  282. }
  283. return {
  284. addTests: addTests,
  285. data2Attribute: data2Attr,
  286. getAttributeData: getAttrData,
  287. fixHtml: fixHtml,
  288. getWidgetById: getWidgetById,
  289. getWidgetByDOMOffset: getWidgetByDOMOffset,
  290. classes2Array: classes2Array,
  291. assertDowncast: assertDowncast,
  292. assertWidgetDialog: assertWidgetDialog,
  293. assertWidget: assertWidget,
  294. widgetInitedWrapperAttributes:
  295. 'aria-label="[a-z]+ widget" ' +
  296. 'class="cke_widget_wrapper cke_widget_(?:inline|block) cke_widget_test(?:_upcasted_pasting|inline|block)" ' +
  297. 'contenteditable="false" ' +
  298. '(?:data-cke-display-name="[a-z0-9]+" )?' +
  299. '(?:data-cke-expando="[0-9]+" )?' +
  300. 'data-cke-filter="off" ' +
  301. 'data-cke-widget-id="[0-9]+" ' +
  302. 'data-cke-widget-wrapper="1" ' +
  303. 'role="region" ' +
  304. 'tabindex="-1"',
  305. widgetWrapperAttributes:
  306. 'class="cke_widget_wrapper cke_widget_new cke_widget_(?:inline|block) cke_widget_test(?:1|2|inline|block)?" ' +
  307. 'contenteditable="false" ' +
  308. '(?:data-cke-display-name="[a-z0-9]+" )?' +
  309. '(?:data-cke-expando="[0-9]+" )?' +
  310. 'data-cke-filter="off" ' +
  311. 'data-cke-widget-wrapper="1" ' +
  312. 'tabindex="-1"',
  313. widgetDragHanlder:
  314. '<span class="cke_reset cke_widget_drag_handler_container" style="[^"]+">' +
  315. '<img class="cke_reset cke_widget_drag_handler" ' +
  316. '(?:data-cke-expando="[0-9]+" )?' +
  317. 'data-cke-widget-drag-handler="1" ' +
  318. '(?:draggable="true" )?' +
  319. 'height="\\d+" ' +
  320. 'role="presentation" ' +
  321. 'src="[^"]+" ' +
  322. 'title="[^"]+" ' +
  323. 'width="\\d+" ' +
  324. '/>' +
  325. '</span>'
  326. };
  327. } )();