ソースを参照

Merge branch 'master' into t/ckeditor5/1449

Krzysztof Krztoń 7 年 前
コミット
ad9943a116

+ 5 - 6
packages/ckeditor5-core/.travis.yml

@@ -11,17 +11,16 @@ language: node_js
 node_js:
 - '8'
 cache:
-- node_modules
+  yarn: true
 branches:
-   except:
-   - stable
+  except:
+  - stable
 before_install:
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start
-- npm config set package-lock false
-- npm i -g npm@^5.7.1
+- npm i -g yarn
 install:
-- npm install @ckeditor/ckeditor5-dev-tests
+- yarn add @ckeditor/ckeditor5-dev-tests
 - ckeditor5-dev-tests-install-dependencies
 script:
 - ckeditor5-dev-tests-travis

+ 14 - 0
packages/ckeditor5-core/lang/translations/az.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Azerbaijani (https://www.transifex.com/ckeditor/teams/11143/az/)\n"
+"Language: az\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the Save button."
+msgid "Save"
+msgstr "Yadda saxla"
+
+msgctxt "Label for the Cancel button."
+msgid "Cancel"
+msgstr "İmtina et"

+ 14 - 0
packages/ckeditor5-core/lang/translations/id.po

@@ -0,0 +1,14 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Indonesian (https://www.transifex.com/ckeditor/teams/11143/id/)\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Label for the Save button."
+msgid "Save"
+msgstr "Simpan"
+
+msgctxt "Label for the Cancel button."
+msgid "Cancel"
+msgstr "Batal"

+ 2 - 0
packages/ckeditor5-core/src/editor/editorui.js

@@ -98,6 +98,8 @@ export default class EditorUI {
 	destroy() {
 		this.stopListening();
 
+		this.focusTracker.destroy();
+
 		this._editableElements = new Map();
 	}
 

+ 147 - 0
packages/ckeditor5-core/tests/_utils/memory.js

@@ -0,0 +1,147 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global window, document, setTimeout */
+
+const TEST_TIMEOUT = 5000;
+const GARBAGE_COLLECTOR_TIMEOUT = 500;
+
+/**
+ * Memory tests suite definition that:
+ * - skips tests when garbage collector is not available.
+ * - creates/destroys editor element (id = 'mem-editor').
+ *
+ * This method should be used with dedicated memory usage test case functions:
+ *
+ *        describe( 'editor', () => {
+ *			// Other tests.
+ *
+ *			describeMemoryUsage( () => {
+ *				testMemoryUsage( 'and editor', () => {
+ *					return ClassicEditor.create( document.querySelector( '#mem-editor' ) );
+ *				} );
+ *			} );
+ *		} );
+ *
+ * @param {Function} callback Callback with test suit body
+ */
+export function describeMemoryUsage( callback ) {
+	describe( 'memory usage', () => {
+		skipIfNoGarbageCollector();
+
+		beforeEach( createEditorElement );
+
+		afterEach( destroyEditorElement );
+
+		callback();
+	} );
+}
+
+/**
+ * Single test case for memory usage test. This method will handle memory usage test procedure:
+ * - creating editor instance
+ * - recording its memory usage (after garbage collector)
+ * - create and destroy editor 10 times
+ * - record memory usage after final editor destroy (after garbage collector)
+ * - tests if memory grew
+ *
+ * See {@link describeMemoryUsage} function for usage details.
+ *
+ * @param {String} testName Name of a test case.
+ * @param {Function} editorCreator Callback which creates editor and returns it's `.create()` promise.
+ */
+export function testMemoryUsage( testName, editorCreator ) {
+	it( testName, function() {
+		this.timeout( TEST_TIMEOUT );
+
+		return runTest( editorCreator );
+	} );
+}
+
+// Runs a single test case. This method will properly setup memory-leak test:
+// - create editor
+// - run garbage collector
+// - record memory allocations
+// - destroy the editor
+// - create & destroy editor multiple times (9) - after each editor creation the test runner will be paused for ~200ms
+function runTest( editorCreator ) {
+	let memoryAfterFirstStart;
+
+	return Promise.resolve() // Promise.resolve just to align below code.
+	// First editor creation needed to load all editor code,css into the memory (it is reported as used heap memory)
+		.then( editorCreator )
+		.then( editor => {
+			return collectMemoryStats().then( mem => {
+				memoryAfterFirstStart = mem;
+
+				return editor;
+			} );
+		} )
+		.then( destroy )
+		// Run create-wait-destroy multiple times. Multiple runs to grow memory significantly even on smaller leaks.
+		.then( createAndDestroy ) // #2
+		.then( createAndDestroy ) // #3
+		.then( collectMemoryStats )
+		.then( memory => {
+			const memoryDifference = memory.usedJSHeapSize - memoryAfterFirstStart.usedJSHeapSize;
+
+			expect( memoryDifference, 'used heap size should not grow' ).to.be.at.most( 0 );
+		} );
+
+	function createAndDestroy() {
+		return Promise.resolve()
+			.then( editorCreator )
+			.then( destroy );
+	}
+}
+
+function createEditorElement() {
+	const editorElement = document.createElement( 'div' );
+	editorElement.id = 'mem-editor';
+
+	editorElement.innerHTML =
+		'<h2>Editor 1</h2>\n' +
+		'<p>This is an editor instance. And there\'s <a href="http://ckeditor.com">some link</a>.</p>';
+
+	document.body.appendChild( editorElement );
+}
+
+function destroyEditorElement() {
+	document.getElementById( 'mem-editor' ).remove();
+}
+
+function destroy( editor ) {
+	return editor.destroy();
+}
+
+function collectMemoryStats() {
+	return new Promise( resolve => {
+		// Enforce garbage collection before recording memory stats.
+		window.gc();
+
+		setTimeout( () => {
+			const memeInfo = window.performance.memory;
+
+			resolve( {
+				totalJSHeapSize: memeInfo.totalJSHeapSize,
+				usedJSHeapSize: memeInfo.usedJSHeapSize,
+				jsHeapSizeLimit: memeInfo.jsHeapSizeLimit
+			} );
+		}, GARBAGE_COLLECTOR_TIMEOUT );
+	} );
+}
+
+// Will skip test suite if not in compatible browser.
+// Currently on Google Chrome supports this method and must be run with proper flags:
+//
+// 		google-chrome -js-flags="--expose-gc"
+//
+function skipIfNoGarbageCollector() {
+	before( function() {
+		if ( !window.gc ) {
+			this.skip();
+		}
+	} );
+}