瀏覽代碼

Update model/view position docs and tests.

Maciej Gołaszewski 7 年之前
父節點
當前提交
f4fd8ff443

+ 2 - 1
packages/ckeditor5-engine/src/model/documentselection.js

@@ -332,7 +332,8 @@ export default class DocumentSelection {
 	 * Moves {@link module:engine/model/documentselection~DocumentSelection#focus} to the specified location.
 	 * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionFocus} method.
 	 *
-	 * The location can be specified in the same form as {@link module:engine/model/position~Position._createAt} parameters.
+	 * The location can be specified in the same form as
+	 * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.
 	 *
 	 * @see module:engine/model/writer~Writer#setSelectionFocus
 	 * @protected

+ 4 - 11
packages/ckeditor5-engine/src/model/liveposition.js

@@ -77,7 +77,8 @@ export default class LivePosition extends Position {
 
 	/**
 	 * @static
-	 * @method module:engine/model/liveposition~LivePosition.createAfter
+	 * @protected
+	 * @method module:engine/model/liveposition~LivePosition._createAfter
 	 * @see module:engine/model/position~Position._createAfter
 	 * @param {module:engine/model/node~Node} node
 	 * @returns {module:engine/model/liveposition~LivePosition}
@@ -85,21 +86,13 @@ export default class LivePosition extends Position {
 
 	/**
 	 * @static
-	 * @method module:engine/model/liveposition~LivePosition.createBefore
+	 * @protected
+	 * @method module:engine/model/liveposition~LivePosition._createBefore
 	 * @see module:engine/model/position~Position._createBefore
 	 * @param {module:engine/model/node~Node} node
 	 * @returns {module:engine/model/liveposition~LivePosition}
 	 */
 
-	/**
-	 * @static
-	 * @method module:engine/model/liveposition~LivePosition.createFromParentAndOffset
-	 * @see module:engine/model/position~Position.createFromParentAndOffset
-	 * @param {module:engine/model/element~Element} parent
-	 * @param {Number} offset
-	 * @returns {module:engine/model/liveposition~LivePosition}
-	 */
-
 	/**
 	 * Fired when `LivePosition` instance is changed due to changes on {@link module:engine/model/document~Document}.
 	 *

+ 2 - 2
packages/ckeditor5-engine/src/model/model.js

@@ -496,8 +496,8 @@ export default class Model {
 	 *
 	 * This method is a shortcut to other factory methods such as:
 	 *
-	 * * {@link module:engine/model/model~Model#createBefore},
-	 * * {@link module:engine/model/model~Model#createAfter}.
+	 * * {@link module:engine/model/model~Model#createPositionBefore},
+	 * * {@link module:engine/model/model~Model#createPositionAfter}.
 	 *
 	 * **Note:** This method is also available on `writer` instance as
 	 * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()}:

+ 2 - 1
packages/ckeditor5-engine/src/model/selection.js

@@ -481,7 +481,8 @@ export default class Selection {
 	/**
 	 * Moves {@link module:engine/model/selection~Selection#focus} to the specified location.
 	 *
-	 * The location can be specified in the same form as {@link module:engine/model/position~Position._createAt} parameters.
+	 * The location can be specified in the same form as
+	 * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.
 	 *
 	 * @fires change:range
 	 * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition

+ 2 - 2
packages/ckeditor5-engine/src/model/treewalker.js

@@ -380,9 +380,9 @@ function formatReturnValue( type, item, previousPosition, nextPosition, length )
  * @property {module:engine/model/position~Position} previousPosition Previous position of the iterator.
  * * Forward iteration: For `'elementEnd'` it is the last position inside the element. For all other types it is the
  * position before the item. Note that it is more efficient to use this position then calculate the position before
- * the node using {@link module:engine/model/position~Position._createBefore}. It is also more efficient to get the
+ * the node using {@link module:engine/model/model~createPositionBefore}. It is also more efficient to get the
  * position after node by shifting `previousPosition` by `length`, using {@link module:engine/model/position~Position#getShiftedBy},
- * then calculate the position using {@link module:engine/model/position~Position._createAfter}.
+ * then calculate the position using {@link module:engine/model/model~Model#createPositionAfter}.
  * * Backward iteration: For `'elementStart'` it is the first position inside the element. For all other types it is
  * the position after item.
  * @property {module:engine/model/position~Position} nextPosition Next position of the iterator.

+ 2 - 1
packages/ckeditor5-engine/src/view/documentselection.js

@@ -352,7 +352,8 @@ export default class DocumentSelection {
 	/**
 	 * Moves {@link #focus} to the specified location.
 	 *
-	 * The location can be specified in the same form as {@link module:engine/view/position~Position._createAt} parameters.
+	 * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
+	 * parameters.
 	 *
 	 * @protected
 	 * @fires change

+ 2 - 1
packages/ckeditor5-engine/src/view/downcastwriter.js

@@ -121,7 +121,8 @@ export default class DowncastWriter {
 	/**
 	 * Moves {@link module:engine/view/documentselection~DocumentSelection#focus selection's focus} to the specified location.
 	 *
-	 * The location can be specified in the same form as {@link module:engine/view/position~Position._createAt} parameters.
+	 * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
+	 * parameters.
 	 *
 	 * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition
 	 * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when

+ 0 - 11
packages/ckeditor5-engine/src/view/position.js

@@ -380,17 +380,6 @@ export default class Position {
 
 		return new Position( item.parent, item.index );
 	}
-
-	/**
-	 * Creates and returns a new instance of `Position`, which is equal to the passed position.
-	 *
-	 * @protected
-	 * @param {module:engine/view/position~Position} position Position to be cloned.
-	 * @returns {module:engine/view/position~Position}
-	 */
-	static _createFromPosition( position ) {
-		return new this( position.parent, position.offset );
-	}
 }
 
 /**

+ 2 - 1
packages/ckeditor5-engine/src/view/selection.js

@@ -548,7 +548,8 @@ export default class Selection {
 	/**
 	 * Moves {@link #focus} to the specified location.
 	 *
-	 * The location can be specified in the same form as {@link module:engine/view/position~Position._createAt} parameters.
+	 * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
+	 * parameters.
 	 *
 	 * @fires change
 	 * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition

+ 1 - 1
packages/ckeditor5-engine/src/view/treewalker.js

@@ -468,7 +468,7 @@ export default class TreeWalker {
  * @property {module:engine/view/position~Position} previousPosition Previous position of the iterator.
  * * Forward iteration: For `'elementEnd'` it is the last position inside the element. For all other types it is the
  * position before the item. Note that it is more efficient to use this position then calculate the position before
- * the node using {@link module:engine/view/position~Position._createBefore}.
+ * the node using {@link module:engine/view/view~view.createPositionBefore}.
  * * Backward iteration: For `'elementStart'` it is the first position inside the element. For all other types it is
  * the position after item.
  * * If the position is at the beginning or at the end of the {@link module:engine/view/text~Text} it is always moved from the

+ 78 - 76
packages/ckeditor5-engine/tests/model/position.js

@@ -126,112 +126,114 @@ describe( 'Position', () => {
 		} );
 	} );
 
-	describe( '_createAt()', () => {
-		it( 'should throw if no offset is passed', () => {
-			expect( () => Position._createAt( ul ) ).to.throw( CKEditorError, /model-position-createAt-required-second-parameter/ );
-		} );
+	describe( 'static creators', () => {
+		describe( '_createAt()', () => {
+			it( 'should throw if no offset is passed', () => {
+				expect( () => Position._createAt( ul ) ).to.throw( CKEditorError, /model-position-createAt-required-second-parameter/ );
+			} );
 
-		it( 'should create positions from positions', () => {
-			const position = Position._createAt( ul, 0 );
+			it( 'should create positions from positions', () => {
+				const position = Position._createAt( ul, 0 );
 
-			const positionCopy = Position._createAt( position );
+				const positionCopy = Position._createAt( position );
 
-			expect( positionCopy ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
-			expect( positionCopy ).to.have.property( 'root' ).that.equals( position.root );
-			expect( positionCopy ).to.not.equal( position );
-		} );
+				expect( positionCopy ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
+				expect( positionCopy ).to.have.property( 'root' ).that.equals( position.root );
+				expect( positionCopy ).to.not.equal( position );
+			} );
 
-		it( 'should create positions from node and offset', () => {
-			expect( Position._createAt( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
-			expect( Position._createAt( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
-			expect( Position._createAt( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
+			it( 'should create positions from node and offset', () => {
+				expect( Position._createAt( root, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+				expect( Position._createAt( root, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+				expect( Position._createAt( root, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 
-			expect( Position._createAt( p, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0, 0 ] );
+				expect( Position._createAt( p, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 0, 0 ] );
 
-			expect( Position._createAt( ul, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
-			expect( Position._createAt( ul, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
-			expect( Position._createAt( ul, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
+				expect( Position._createAt( ul, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
+				expect( Position._createAt( ul, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+				expect( Position._createAt( ul, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
 
-			expect( Position._createAt( li1, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
-			expect( Position._createAt( li1, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
-			expect( Position._createAt( li1, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
-			expect( Position._createAt( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
-		} );
+				expect( Position._createAt( li1, 0 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
+				expect( Position._createAt( li1, 1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+				expect( Position._createAt( li1, 2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+				expect( Position._createAt( li1, 3 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
+			} );
 
-		it( 'should create positions from node and flag', () => {
-			expect( Position._createAt( root, 'end' ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
+			it( 'should create positions from node and flag', () => {
+				expect( Position._createAt( root, 'end' ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 
-			expect( Position._createAt( p, 'before' ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
-			expect( Position._createAt( a, 'before' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
+				expect( Position._createAt( p, 'before' ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+				expect( Position._createAt( a, 'before' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
 
-			expect( Position._createAt( p, 'after' ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
-			expect( Position._createAt( a, 'after' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
+				expect( Position._createAt( p, 'after' ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+				expect( Position._createAt( a, 'after' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
 
-			expect( Position._createAt( ul, 'end' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
-		} );
+				expect( Position._createAt( ul, 'end' ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
+			} );
 
-		it( 'throws when parent is not an element', () => {
-			expect( () => {
-				Position._createAt( b, 0 );
-			} ).to.throw( CKEditorError, /^model-position-parent-incorrect/ );
-		} );
+			it( 'throws when parent is not an element', () => {
+				expect( () => {
+					Position._createAt( b, 0 );
+				} ).to.throw( CKEditorError, /^model-position-parent-incorrect/ );
+			} );
 
-		it( 'works with a doc frag', () => {
-			const frag = new DocumentFragment();
+			it( 'works with a doc frag', () => {
+				const frag = new DocumentFragment();
 
-			expect( Position._createAt( frag, 0 ) ).to.have.property( 'root', frag );
+				expect( Position._createAt( frag, 0 ) ).to.have.property( 'root', frag );
+			} );
 		} );
-	} );
 
-	describe( 'createBefore()', () => {
-		it( 'should create positions before elements', () => {
-			expect( Position._createBefore( p ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
+		describe( '_createBefore()', () => {
+			it( 'should create positions before elements', () => {
+				expect( Position._createBefore( p ) ).to.have.property( 'path' ).that.deep.equals( [ 0 ] );
 
-			expect( Position._createBefore( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+				expect( Position._createBefore( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 
-			expect( Position._createBefore( li1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
+				expect( Position._createBefore( li1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0 ] );
 
-			expect( Position._createBefore( f ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
-			expect( Position._createBefore( o ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
-			expect( Position._createBefore( z ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+				expect( Position._createBefore( f ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 0 ] );
+				expect( Position._createBefore( o ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+				expect( Position._createBefore( z ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
 
-			expect( Position._createBefore( li2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+				expect( Position._createBefore( li2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
 
-			expect( Position._createBefore( b ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 0 ] );
-			expect( Position._createBefore( a ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
-			expect( Position._createBefore( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
-		} );
+				expect( Position._createBefore( b ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 0 ] );
+				expect( Position._createBefore( a ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
+				expect( Position._createBefore( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
+			} );
 
-		it( 'should throw error if one try to create positions before root', () => {
-			expect( () => {
-				Position._createBefore( root );
-			} ).to.throw( CKEditorError, /model-position-before-root/ );
+			it( 'should throw error if one try to create positions before root', () => {
+				expect( () => {
+					Position._createBefore( root );
+				} ).to.throw( CKEditorError, /model-position-before-root/ );
+			} );
 		} );
-	} );
 
-	describe( 'createAfter()', () => {
-		it( 'should create positions after elements', () => {
-			expect( Position._createAfter( p ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
+		describe( '_createAfter()', () => {
+			it( 'should create positions after elements', () => {
+				expect( Position._createAfter( p ) ).to.have.property( 'path' ).that.deep.equals( [ 1 ] );
 
-			expect( Position._createAfter( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
+				expect( Position._createAfter( ul ) ).to.have.property( 'path' ).that.deep.equals( [ 2 ] );
 
-			expect( Position._createAfter( li1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
+				expect( Position._createAfter( li1 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1 ] );
 
-			expect( Position._createAfter( f ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
-			expect( Position._createAfter( o ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
-			expect( Position._createAfter( z ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
+				expect( Position._createAfter( f ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 1 ] );
+				expect( Position._createAfter( o ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 2 ] );
+				expect( Position._createAfter( z ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 0, 3 ] );
 
-			expect( Position._createAfter( li2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
+				expect( Position._createAfter( li2 ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 2 ] );
 
-			expect( Position._createAfter( b ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
-			expect( Position._createAfter( a ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
-			expect( Position._createAfter( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 3 ] );
-		} );
+				expect( Position._createAfter( b ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 1 ] );
+				expect( Position._createAfter( a ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 2 ] );
+				expect( Position._createAfter( r ) ).to.have.property( 'path' ).that.deep.equals( [ 1, 1, 3 ] );
+			} );
 
-		it( 'should throw error if one try to make positions after root', () => {
-			expect( () => {
-				Position._createAfter( root );
-			} ).to.throw( CKEditorError, /model-position-after-root/ );
+			it( 'should throw error if one try to make positions after root', () => {
+				expect( () => {
+					Position._createAfter( root );
+				} ).to.throw( CKEditorError, /model-position-after-root/ );
+			} );
 		} );
 	} );
 

+ 158 - 102
packages/ckeditor5-engine/tests/view/position.js

@@ -15,6 +15,10 @@ import TextProxy from '../../src/view/textproxy';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 import { parse, stringify } from '../../src/dev-utils/view';
+import TreeWalker from '../../src/view/treewalker';
+import createViewRoot from './_utils/createroot';
+import AttributeElement from '../../src/view/attributeelement';
+import ContainerElement from '../../src/view/containerelement';
 
 describe( 'Position', () => {
 	const parentMock = {};
@@ -151,74 +155,6 @@ describe( 'Position', () => {
 		} );
 	} );
 
-	describe( '_createAt()', () => {
-		it( 'should throw if no offset is passed', () => {
-			const element = new Element( 'p' );
-
-			expect( () => Position._createAt( element ) ).to.throw( CKEditorError, /view-position-createAt-required-second-parameter/ );
-		} );
-
-		it( 'should create positions from positions', () => {
-			const p = new Element( 'p' );
-			const position = new Position( p, 0 );
-			const created = Position._createAt( position, 0 );
-
-			expect( created.isEqual( position ) ).to.be.true;
-			expect( created ).to.not.be.equal( position );
-		} );
-
-		it( 'should create positions from node and offset', () => {
-			const foo = new Text( 'foo' );
-			const p = new Element( 'p', null, foo );
-
-			expect( Position._createAt( foo, 0 ).parent ).to.equal( foo );
-			expect( Position._createAt( foo, 0 ).offset ).to.equal( 0 );
-
-			expect( Position._createAt( foo, 2 ).parent ).to.equal( foo );
-			expect( Position._createAt( foo, 2 ).offset ).to.equal( 2 );
-
-			expect( Position._createAt( p, 1 ).parent ).to.equal( p );
-			expect( Position._createAt( p, 1 ).offset ).to.equal( 1 );
-		} );
-
-		it( 'should create positions from node and flag', () => {
-			const foo = new Text( 'foo' );
-			const p = new Element( 'p', null, foo );
-
-			const fooEnd = Position._createAt( foo, 'end' );
-			const fooBefore = Position._createAt( foo, 'before' );
-			const fooAfter = Position._createAt( foo, 'after' );
-
-			const pEnd = Position._createAt( p, 'end' );
-			// pBefore and pAfter would throw.
-
-			expect( fooEnd.parent ).to.equal( foo );
-			expect( fooEnd.offset ).to.equal( 3 );
-
-			expect( fooBefore.parent ).to.equal( p );
-			expect( fooBefore.offset ).to.equal( 0 );
-
-			expect( fooAfter.parent ).to.equal( p );
-			expect( fooAfter.offset ).to.equal( 1 );
-
-			expect( pEnd.parent ).to.equal( p );
-			expect( pEnd.offset ).to.equal( 1 );
-		} );
-
-		it( 'should create positions in document fragment', () => {
-			const foo = new Text( 'foo' );
-			const docFrag = new DocumentFragment( [ foo ] );
-
-			const pStart = Position._createAt( docFrag, 0 );
-			const pEnd = Position._createAt( docFrag, 'end' );
-
-			expect( pStart.parent ).to.equal( docFrag );
-			expect( pStart.offset ).to.equal( 0 );
-			expect( pEnd.parent ).to.equal( docFrag );
-			expect( pEnd.offset ).to.equal( 1 );
-		} );
-	} );
-
 	describe( 'isEqual', () => {
 		it( 'should return true for same object', () => {
 			const position = new Position( {}, 12 );
@@ -442,53 +378,123 @@ describe( 'Position', () => {
 		} );
 	} );
 
-	describe( '_createBefore()', () => {
-		it( 'should throw error if one try to create positions before root', () => {
-			expect( () => {
-				Position._createBefore( parse( '<p></p>' ) );
-			} ).to.throw( CKEditorError, /view-position-before-root/ );
-		} );
+	describe( 'static creators', () => {
+		describe( '_createAt()', () => {
+			it( 'should throw if no offset is passed', () => {
+				const element = new Element( 'p' );
 
-		it( 'should create positions before `Node`', () => {
-			const { selection } = parse( '<p>[]<b></b></p>' );
-			const position = selection.getFirstPosition();
-			const nodeAfter = position.nodeAfter;
+				expect( () => Position._createAt( element ) ).to.throw( CKEditorError, /view-position-createAt-required-second-parameter/ );
+			} );
 
-			expect( Position._createBefore( nodeAfter ).isEqual( position ) ).to.be.true;
-		} );
+			it( 'should create positions from positions', () => {
+				const p = new Element( 'p' );
+				const position = new Position( p, 0 );
+				const created = Position._createAt( position, 0 );
 
-		it( 'should create positions before `TextProxy`', () => {
-			const text = new Text( 'abc' );
+				expect( created.isEqual( position ) ).to.be.true;
+				expect( created ).to.not.be.equal( position );
+			} );
 
-			const textProxy = new TextProxy( text, 1, 1 );
-			const position = new Position( text, 1 );
+			it( 'should create positions from node and offset', () => {
+				const foo = new Text( 'foo' );
+				const p = new Element( 'p', null, foo );
 
-			expect( Position._createBefore( textProxy ) ).deep.equal( position );
-		} );
-	} );
+				expect( Position._createAt( foo, 0 ).parent ).to.equal( foo );
+				expect( Position._createAt( foo, 0 ).offset ).to.equal( 0 );
+
+				expect( Position._createAt( foo, 2 ).parent ).to.equal( foo );
+				expect( Position._createAt( foo, 2 ).offset ).to.equal( 2 );
+
+				expect( Position._createAt( p, 1 ).parent ).to.equal( p );
+				expect( Position._createAt( p, 1 ).offset ).to.equal( 1 );
+			} );
 
-	describe( '_createAfter()', () => {
-		it( 'should throw error if one try to create positions after root', () => {
-			expect( () => {
-				Position._createAfter( parse( '<p></p>' ) );
-			} ).to.throw( CKEditorError, /view-position-after-root/ );
+			it( 'should create positions from node and flag', () => {
+				const foo = new Text( 'foo' );
+				const p = new Element( 'p', null, foo );
+
+				const fooEnd = Position._createAt( foo, 'end' );
+				const fooBefore = Position._createAt( foo, 'before' );
+				const fooAfter = Position._createAt( foo, 'after' );
+
+				const pEnd = Position._createAt( p, 'end' );
+				// pBefore and pAfter would throw.
+
+				expect( fooEnd.parent ).to.equal( foo );
+				expect( fooEnd.offset ).to.equal( 3 );
+
+				expect( fooBefore.parent ).to.equal( p );
+				expect( fooBefore.offset ).to.equal( 0 );
+
+				expect( fooAfter.parent ).to.equal( p );
+				expect( fooAfter.offset ).to.equal( 1 );
+
+				expect( pEnd.parent ).to.equal( p );
+				expect( pEnd.offset ).to.equal( 1 );
+			} );
+
+			it( 'should create positions in document fragment', () => {
+				const foo = new Text( 'foo' );
+				const docFrag = new DocumentFragment( [ foo ] );
+
+				const pStart = Position._createAt( docFrag, 0 );
+				const pEnd = Position._createAt( docFrag, 'end' );
+
+				expect( pStart.parent ).to.equal( docFrag );
+				expect( pStart.offset ).to.equal( 0 );
+				expect( pEnd.parent ).to.equal( docFrag );
+				expect( pEnd.offset ).to.equal( 1 );
+			} );
 		} );
 
-		it( 'should create positions after `Node`', () => {
-			const { selection } = parse( '<p><b></b>[]</p>' );
-			const position = selection.getFirstPosition();
-			const nodeBefore = position.nodeBefore;
+		describe( '_createBefore()', () => {
+			it( 'should throw error if one try to create positions before root', () => {
+				expect( () => {
+					Position._createBefore( parse( '<p></p>' ) );
+				} ).to.throw( CKEditorError, /view-position-before-root/ );
+			} );
 
-			expect( Position._createAfter( nodeBefore ).isEqual( position ) ).to.be.true;
+			it( 'should create positions before `Node`', () => {
+				const { selection } = parse( '<p>[]<b></b></p>' );
+				const position = selection.getFirstPosition();
+				const nodeAfter = position.nodeAfter;
+
+				expect( Position._createBefore( nodeAfter ).isEqual( position ) ).to.be.true;
+			} );
+
+			it( 'should create positions before `TextProxy`', () => {
+				const text = new Text( 'abc' );
+
+				const textProxy = new TextProxy( text, 1, 1 );
+				const position = new Position( text, 1 );
+
+				expect( Position._createBefore( textProxy ) ).deep.equal( position );
+			} );
 		} );
 
-		it( 'should create positions after `TextProxy`', () => {
-			const text = new Text( 'abcd' );
+		describe( '_createAfter()', () => {
+			it( 'should throw error if one try to create positions after root', () => {
+				expect( () => {
+					Position._createAfter( parse( '<p></p>' ) );
+				} ).to.throw( CKEditorError, /view-position-after-root/ );
+			} );
 
-			const textProxy = new TextProxy( text, 1, 2 );
-			const position = new Position( text, 3 );
+			it( 'should create positions after `Node`', () => {
+				const { selection } = parse( '<p><b></b>[]</p>' );
+				const position = selection.getFirstPosition();
+				const nodeBefore = position.nodeBefore;
 
-			expect( Position._createAfter( textProxy ) ).deep.equal( position );
+				expect( Position._createAfter( nodeBefore ).isEqual( position ) ).to.be.true;
+			} );
+
+			it( 'should create positions after `TextProxy`', () => {
+				const text = new Text( 'abcd' );
+
+				const textProxy = new TextProxy( text, 1, 2 );
+				const position = new Position( text, 3 );
+
+				expect( Position._createAfter( textProxy ) ).deep.equal( position );
+			} );
 		} );
 	} );
 
@@ -597,4 +603,54 @@ describe( 'Position', () => {
 			expect( positionB.getCommonAncestor( positionA ) ).to.equal( lca );
 		}
 	} );
+
+	describe( 'getWalker()', () => {
+		let root;
+
+		beforeEach( () => {
+			const doc = new Document();
+
+			root = createViewRoot( doc );
+
+			const textAbcd = new Text( 'abcd' );
+			const bold = new AttributeElement( 'b', null, [ textAbcd ] );
+
+			const paragraph = new ContainerElement( 'p', null, [ bold ] );
+			const img = new ContainerElement( 'img' );
+
+			root._insertChild( 0, [ img, paragraph ] );
+		} );
+
+		it( 'should be possible to iterate using this method', () => {
+			const position = new Position( root, 0 );
+
+			const items = [];
+			const walker = position.getWalker();
+
+			for ( const value of walker ) {
+				items.push( value.type + ':' + ( value.item.name || value.item.data ) );
+			}
+
+			expect( items ).to.deep.equal( [
+				'elementStart:img',
+				'elementEnd:img',
+				'elementStart:p',
+				'elementStart:b',
+				'text:abcd',
+				'elementEnd:b',
+				'elementEnd:p'
+			] );
+		} );
+
+		it( 'should return treewalker with given options', () => {
+			const position = new Position( root, 0 );
+			const walker = position.getWalker( { singleCharacters: true } );
+
+			expect( walker ).to.be.instanceof( TreeWalker );
+			expect( walker ).to.have.property( 'singleCharacters' ).that.is.true;
+			expect( walker ).to.have.property( 'position' );
+			expect( walker.position.isEqual( position ) ).to.be.true;
+			expect( walker ).to.have.property( 'shallow' ).that.is.false;
+		} );
+	} );
 } );