EBernhardson has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/381510 )

Change subject: Implement prefix_search_api.feature in nodejs tests
......................................................................

Implement prefix_search_api.feature in nodejs tests

* Adds a new withApi() function that attaches more error information
  to exceptions from api.
* Implements a bunch of steps needed by prefix_search_api.feature
* Stores api errors on world so we can test them

Change-Id: Idbf43b536cd5185d32c4b5988f4daa0f1d6d319c
---
M Gruntfile.js
A tests/integration/features/prefix_search_api.feature
M tests/integration/features/step_definitions/page_step_helpers.js
M tests/integration/features/step_definitions/page_steps.js
M tests/integration/features/support/hooks.js
M tests/integration/features/support/world.js
6 files changed, 480 insertions(+), 23 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/CirrusSearch 
refs/changes/10/381510/1

diff --git a/Gruntfile.js b/Gruntfile.js
index 5d38ddf..02fda03 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,3 +1,4 @@
+/*jshint esversion: 6,  node:true */
 /*!
  * Grunt file
  *
@@ -58,14 +59,16 @@
                webdriver: {
                        test: {
                                configFile: WebdriverIOconfigFile,
-                               spec: (() => {
+                               spec: ( () => {
                                        let spec = grunt.option( 'spec' );
-                                       return !spec
-                                               ? undefined
-                                               : (spec[0] === '/'
-                                                       ? spec
-                                                       : path.join(__dirname, 
'tests/integration/features', spec))
-                               })()
+                                       if (!spec) {
+                                               return undefined;
+                                       }
+                                       if ( spec[0] === '/' ) {
+                                               return spec;
+                                       }
+                                       return path.join(__dirname, 
'tests/integration/features', spec);
+                               } )()
                        }
                }
        } );
diff --git a/tests/integration/features/prefix_search_api.feature 
b/tests/integration/features/prefix_search_api.feature
new file mode 100644
index 0000000..e84e830
--- /dev/null
+++ b/tests/integration/features/prefix_search_api.feature
@@ -0,0 +1,157 @@
+@clean @api @prefix @redirect @accent_squashing @accented_namespace @suggest
+Feature: Prefix search via api
+# @suggest needs to be at the end because it will update the completion 
suggester index
+  Scenario: Suggestions don't appear when you search for a string that is too 
long
+    When I get api suggestions for 
贵州省瞬时速度团头鲂身体c实施ysstsstsg说tyttxy以推销员会同香港推广系统在同他讨厌她团体淘汰>赛系统大选于它拥有一天天用于与体育学院国ttxzyttxtxytdttyyyztdsytstsstxtttd天天体育系统的摄像头听到他他偷笑>偷笑太阳团体杏眼桃腮他要tttxx
 
y贵州省瞬时速度团头鲂身体c实施ysstsstsg说tyttxy以推销员会同香港推广系统在同他讨厌她团体淘汰>赛系统大选于它拥有一天天用于与体育学院国ttxzyttxtxytdttyyyztdsytstsstxtttd天天体育系统的摄像头听到他他偷笑>偷笑太阳团体杏眼桃腮他要tttxx
 y
+#    Then the api warns Prefix search request was longer than the maximum 
allowed length. (288 > 255)
+     Then the api returns error code request_too_long
+
+  Scenario: Prefix search lists page name if both redirect and page name match
+    When I get api suggestions for Redirecttest Y using the classic profile
+    Then Redirecttest Yay is the first api suggestion
+      And Redirecttest Yikes is not in the api suggestions
+    When I get api suggestions for Redirecttest Y using the fuzzy profile
+    Then Redirecttest Yay is the first api suggestion
+      And Redirecttest Yikes is not in the api suggestions
+
+  Scenario: Prefix search ranks redirects under title matches
+    When I get api suggestions for PrefixRedirectRanking using the classic 
profile
+    Then PrefixRedirectRanking 1 is the first api suggestion
+      And PrefixRedirectRanking 2 is the second api suggestion
+    When I get api suggestions for PrefixRedirectRanking using the fuzzy 
profile
+    Then PrefixRedirectRanking 1 is the first api suggestion
+      And PrefixRedirectRanking 2 is the second api suggestion
+
+  Scenario: Prefix search with classic profile is stricter than the fuzzy 
profile
+    When I get api suggestions for PrefixRedirectRankng using the classic 
profile
+    Then the API should produce list of length 0
+    When I get api suggestions for PrefixRedirectRankng using the fuzzy profile
+    Then PrefixRedirectRanking 1 is the first api suggestion
+      And PrefixRedirectRanking 2 is the second api suggestion
+
+  Scenario Outline: Search suggestions with accents
+    When I get api suggestions for <term> using the classic profile
+    Then <first_suggestion> is the first api suggestion
+      And <second_suggestion> is the second api suggestion
+    When I get api suggestions for <term> using the fuzzy profile
+    Then <first_suggestion> is the first api suggestion
+      And <second_suggestion> is the second api suggestion
+  Examples:
+    |      term      | first_suggestion | second_suggestion |
+    | Áccent Sorting | Áccent Sorting   | Accent Sorting    |
+    | áccent Sorting | Áccent Sorting   | Accent Sorting    |
+    | Accent Sorting | Accent Sorting   | Áccent Sorting    |
+    | accent Sorting | Accent Sorting   | Áccent Sorting    |
+
+  Scenario: Searching for a bare namespace finds everything in the namespace
+    Given a page named Template talk:Foo exists
+      And within 20 seconds api searching for Template talk:Foo yields 
Template talk:Foo as the first result
+    When I get api suggestions for template talk:
+    Then Template talk:Foo is in the api suggestions
+
+  Scenario Outline: Search suggestions
+    When I get api suggestions for <term> using the classic profile
+    Then <first_result> is the first api suggestion
+      And the api should offer to search for pages containing <term>
+    When I get api suggestions for <term> using the fuzzy profile
+    Then <first_result> is the first api suggestion
+      And the api should offer to search for pages containing <term>
+    When I get api near matches for <term>
+      Then <title> is the first api search result
+  Examples:
+    | term                   | first_result           | title                  
|
+# Note that there are more links to catapult then to any other page that 
starts with the
+# word "catapult" so it should be first
+    | catapult               | Catapult               | Catapult               
|
+    | catapul                | Catapult               | none                   
|
+    | two words              | Two Words              | Two Words              
|
+#   | ~catapult              | none                   | none                   
|
+    | Template:Template Test | Template:Template Test | Template:Template Test 
|
+    | l'or                   | L'Oréal                | none                   
|
+    | l or                   | L'Oréal                | none                   
|
+    | L'orea                 | L'Oréal                | none                   
|
+    | L'Oréal                | L'Oréal                | L'Oréal                
|
+    | L’Oréal                | L'Oréal                | L'Oréal                
|
+    | L Oréal                | L'Oréal                | L'Oréal                
|
+    | Jean-Yves Le Drian     | Jean-Yves Le Drian     | Jean-Yves Le Drian     
|
+    | Jean Yves Le Drian     | Jean-Yves Le Drian     | Jean-Yves Le Drian     
|
+
+  Scenario: Prefix search includes redirects
+    When I get api suggestions for SEO Redirecttest using the classic profile
+    Then SEO Redirecttest is the first api suggestion
+    When I get api near matches for SEO Redirecttest
+    Then SEO Redirecttest is the first api search result
+    When I get api suggestions for SEO Redirecttest using the fuzzy profile
+    Then SEO Redirecttest is the first api suggestion
+    When I get api near matches for SEO Redirecttest
+    Then SEO Redirecttest is the first api search result
+
+  Scenario: Prefix search includes redirects for pages outside the main 
namespace
+    When I get api suggestions for User_talk:SEO Redirecttest using the 
classic profile
+    Then User talk:SEO Redirecttest is the first api suggestion
+    When I get api near matches for User_talk:SEO Redirecttest
+    Then User talk:SEO Redirecttest is the first api search result
+    When I get api suggestions for User_talk:SEO Redirecttest using the fuzzy 
profile
+    Then User talk:SEO Redirecttest is the first api suggestion
+    When I get api near matches for User_talk:SEO Redirecttest
+    Then User talk:SEO Redirecttest is the first api search result
+
+  Scenario Outline: Search suggestions with accents
+    When I get api suggestions for <term> using the classic profile
+    Then <first_result> is the first api suggestion
+      And the api should offer to search for pages containing <term>
+    When I get api suggestions for <term> using the fuzzy profile
+    Then <first_result> is the first api suggestion
+      And the api should offer to search for pages containing <term>
+    When I get api near matches for <term>
+    Then <title> is the first api search result
+  Examples:
+    | term                   | first_result           | title                  
|
+    | África                 | África                 | África                 
|
+    | Africa                 | África                 | África                 
|
+    | AlphaBeta              | AlphaBeta              | AlphaBeta              
|
+    | ÁlphaBeta              | AlphaBeta              | AlphaBeta              
|
+    | Mó:Test                | Mó:Test                | Mó:Test                
|
+    | Mo:Test                | Mó:Test                | Mó:Test                
|
+    | file:Mo:Test           | none                   | none                   
|
+
+  Scenario Outline: Search suggestions with various profiles
+    When I get api suggestions for <term> using the <profile> profile
+    Then <result>
+      And the api should offer to search for pages containing <term>
+  Examples:
+    | term      | profile           | result                                   
            |
+    | África    | strict            | África is the first api suggestion       
            |
+    | Africa    | strict            | the API should produce list of length 0  
            |
+    | Agrica    | strict            | the API should produce list of length 0  
            |
+    | África    | normal            | África is the first api suggestion       
            |
+    | Africa    | normal            | África is the first api suggestion       
            |
+    | Agrica    | normal            | the API should produce list of length 0  
            |
+    | África    | classic           | África is the first api suggestion       
            |
+    | Africa    | classic           | África is the first api suggestion       
            |
+    | Agrica    | classic           | the API should produce list of length 0  
            |
+    | África    | fuzzy             | África is the first api suggestion       
            |
+    | Africa    | fuzzy             | África is the first api suggestion       
            |
+    | Agrica    | fuzzy             | África is the first api suggestion       
            |
+    | doors     | strict            | the API should produce list of length 0  
            |
+    | doors     | classic           | the API should produce list of length 0  
            |
+    | doors     | normal            | The Doors is the first api suggestion    
            |
+    | the doors | normal            | The Doors is the first api suggestion    
            |
+    | thedoors  | normal            | the API should produce list of length 0  
            |
+    | doors     | fuzzy             | The Doors is the first api suggestion    
            |
+    | the doors | fuzzy             | The Doors is the first api suggestion    
            |
+    | thedoors  | fuzzy             | The Doors is the first api suggestion    
            |
+    | endym     | classic           | the API should produce list of length 0  
            |
+    | endym     | normal            | the API should produce list of length 0  
            |
+    | endym     | fuzzy             | the API should produce list of length 0  
            |
+    | endym     | normal-subphrases | Hyperion Cantos/Endymion is the first 
api suggestion |
+    | endym     | fuzzy-subphrases  | Hyperion Cantos/Endymion is the first 
api suggestion |
+    | endimion  | normal-subphrases | the API should produce list of length 0  
            |
+    | endimion  | fuzzy-subphrases  | Hyperion Cantos/Endymion is the first 
api suggestion |
+  # Just take too long to run on a regular basis
+  # @redirect @huge
+  # Scenario: Prefix search on pages with tons of redirects is reasonably fast
+  #   Given a page named IHaveTonsOfRedirects exists
+  #     And there are 1000 redirects to IHaveTonsOfRedirects of the form 
TonsOfRedirects%s
+  #   When I type TonsOfRedirects into the search box
+  #   Then suggestions should appear
diff --git a/tests/integration/features/step_definitions/page_step_helpers.js 
b/tests/integration/features/step_definitions/page_step_helpers.js
index 2ca50c6..a67761e 100644
--- a/tests/integration/features/step_definitions/page_step_helpers.js
+++ b/tests/integration/features/step_definitions/page_step_helpers.js
@@ -10,7 +10,9 @@
  * https://github.com/cucumber/cucumber-js/issues/634
  */
 
-const expect = require( 'chai' ).expect;
+const expect = require( 'chai' ).expect,
+       fs = require( 'fs' ),
+       path = require( 'path' );
 
 class StepHelpers {
        constructor( world, wiki ) {
@@ -33,10 +35,39 @@
                        } );
                } );
        }
-       editPage( title, content ) {
+
+       editPage( title, text, append = false ) {
                return this.apiPromise.then( ( api ) => {
-                       return api.loginGetEditToken().then( () => {
-                               return api.edit( title, content, "CirrusSearch 
integration test edit" );
+                       if ( text[0] === '@' ) {
+                               text = fs.readFileSync( path.join( __dirname, 
'articles', text.substr( 1 ) ) ).toString();
+                       }
+                       return this.getWikitext( title ).then( ( fetchedText ) 
=> {
+                               if ( append ) {
+                                       text = fetchedText + text;
+                               }
+                               if ( text.trim() !== fetchedText.trim() ) {
+                                       return api.loginGetEditToken().then( () 
=> api.edit( title, text ) );
+                               }
+                       }, ( error ) => {
+                               throw error;
+                       } );
+               } );
+       }
+
+       getWikitext( title ) {
+               return this.apiPromise.then( ( api ) => {
+                       return api.request( {
+                               action: "query",
+                               format: "json",
+                               formatversion: 2,
+                               prop: "revisions",
+                               rvprop: "content",
+                               titles: title
+                       } ).then( ( response ) => {
+                               if ( response.query.pages[0].missing ) {
+                                       return "";
+                               }
+                               return 
response.query.pages[0].revisions[0].content;
                        } );
                } );
        }
@@ -49,9 +80,36 @@
                                cirrusUseCompletionSuggester: 'yes',
                                limit: limit
                        } );
-               } ).then( ( response ) => this.world.setApiResponse( response ) 
);
+               } ).then(
+                       ( response ) => this.world.setApiResponse( response ),
+                       ( error ) => this.world.setApiError( error ) );
        }
 
+       suggestionsWithProfile( query, profile ) {
+               return this.apiPromise.then( ( api ) => {
+                       return api.request( {
+                               action: 'opensearch',
+                               search: query,
+                               profile: profile
+                       } );
+               } ).then(
+                       ( response ) => this.world.setApiResponse( response ),
+                       ( error ) => this.world.setApiError( error ) );
+       }
+
+       searchFor( query, options = {} ) {
+               return this.apiPromise.then( ( api ) => {
+                       return api.request( Object.assign( options, {
+                               action: "query",
+                               list: "search",
+                               srsearch: query,
+                               srprop: 
"snippet|titlesnippet|redirectsnippet|sectionsnippet|categorysnippet|isfilematch",
+                               formatversion: 2
+                       } ) );
+               } ).then(
+                       ( response ) => this.world.setApiResponse( response ),
+                       ( error ) => this.world.setApiError( error ) );
+       }
 }
 
 module.exports = StepHelpers;
diff --git a/tests/integration/features/step_definitions/page_steps.js 
b/tests/integration/features/step_definitions/page_steps.js
index 703857d..c53b403 100644
--- a/tests/integration/features/step_definitions/page_steps.js
+++ b/tests/integration/features/step_definitions/page_steps.js
@@ -10,10 +10,27 @@
  */
 
 const defineSupportCode = require('cucumber').defineSupportCode,
-SpecialVersion = require('../support/pages/special_version'),
+       SpecialVersion = require('../support/pages/special_version'),
        ArticlePage = require('../support/pages/article_page'),
-       expect = require( 'chai' ).expect;
+       expect = require( 'chai' ).expect,
+       querystring = require( 'querystring' );
 
+// Attach extra information to assertion errors about what api call triggered 
the problem
+function withApi( world, fn ) {
+       try {
+               return fn();
+       } catch ( e ) {
+               let request = world.apiResponse ? world.apiResponse.__request : 
world.apiError.request,
+                       qs = Object.assign( {}, request.qs, request.form ),
+                       href = request.uri + '?' + querystring.stringify( qs );
+
+               e.message += `\nLast Api: ${href}`;
+               if ( world.apiError ) {
+                       e.message += `\nError reported: 
${JSON.stringify(world.apiError)}`;
+               }
+               throw e;
+       }
+}
 defineSupportCode( function( {Given, When, Then} ) {
 
        When( /^I go to (.*)$/, function ( title ) {
@@ -33,19 +50,151 @@
        } );
 
        Then( /^the API should produce list containing (.*)/, function( term ) {
-               expect( this.apiResponse[ 1 ] ).to.include( term );
+               withApi( this, () => {
+                       expect( this.apiResponse[ 1 ] ).to.include( term );
+               } );
        } );
 
        Then( /^the API should produce empty list/, function() {
-               expect( this.apiResponse[ 1 ] ).to.have.length( 0 );
+               withApi( this, () => {
+                       expect( this.apiResponse[ 1 ] ).to.have.length( 0 );
+               } );
        } );
 
        Then( /^the API should produce list starting with (.*)/, function( term 
) {
-               expect( this.apiResponse[ 1 ][ 0 ] ).to.equal( term );
+               withApi( this, () => {
+                       expect( this.apiResponse[ 1 ][ 0 ] ).to.equal( term );
+               } );
        } );
 
        Then( /^the API should produce list of length (\d+)/, function( length 
) {
-               expect( this.apiResponse[ 1 ] ).to.have.length( parseInt( 
length, 10 ) );
+               withApi( this, () => {
+                       expect( this.apiResponse[ 1 ] ).to.have.length( 
parseInt( length, 10 ) );
+               } );
+       } );
+
+       When( /^the api returns error code (.*)$/, function ( code ) {
+               withApi( this, () => {
+                       expect( this.apiError ).to.include( {
+                               code: code
+                       } );
+               } );
+       } );
+
+       When( /^I get api suggestions for (.*?)(?: using the (.*) profile)?$/, 
function( search, profile ) {
+               // TODO: Add step helper
+               return this.stepHelpers.suggestionsWithProfile( search, profile 
|| "fuzzy" );
+       } );
+
+       Then( /^(.+) is the (.+) api suggestion$/, function ( title, position ) 
{
+               withApi( this, () => {
+                       let pos = ['first', 'second', 'third', 'fourth', 
'fifth', 'sixth', 'seventh', 'eigth', 'ninth', 'tenth'].indexOf( position );
+                       if ( title === "none" ) {
+                               if ( this.apiError && pos === 1 ) {
+                                       // TODO: Why 1? maybe 0?
+                                       return;
+                               } else {
+                                       expect( this.apiResponse[1] 
).to.have.lengthOf.at.most( pos );
+                               }
+                       } else {
+                               expect( this.apiResponse[1] 
).to.have.lengthOf.at.least( pos );
+                               expect( this.apiResponse[1][pos] ).to.equal( 
title );
+                       }
+               } );
+       } );
+
+       Then( /^(.+) is( not)? in the api suggestions$/, function ( title, 
should_not ) {
+               withApi( this, () => {
+                       if ( should_not ) {
+                               expect( this.apiResponse[1] ).to.not.include( 
title );
+                       } else {
+                               expect( this.apiResponse[1] ).to.include( title 
);
+                       }
+               } );
+       } );
+
+       Then( /^the api should offer to search for pages containing (.+)$/, 
function( term ) {
+               withApi( this, () => {
+                       expect( this.apiResponse[0] ).to.equal( term );
+               } );
+       } );
+
+       When( /^a page named (.+) exists(?: with contents (.+))?$/, function ( 
title, text ) {
+               return this.stepHelpers.editPage( title, text || title, false );
+       } );
+
+       Then( /^I get api near matches for (.+)$/, function ( search ) {
+               return this.stepHelpers.searchFor( search, { srwhat: 
"nearmatch" } );
+       } );
+
+       function checkApiSearchResultStep( title, in_ok, indexes ) {
+               indexes = indexes.split( ' or ' ).map( ( index ) => {
+                       return 'first second third fourth fifth sixth seventh 
eighth ninth tenth'.split( ' ' ).indexOf( index );
+               } );
+               if ( title === "none" ) {
+                       expect( this.apiResponse.query.search 
).to.have.lengthOf.below( 1 + Math.min.apply( null, indexes ) );
+               } else {
+                       withApi( this, () => {
+                               let found = indexes.map( pos => {
+                                       if ( this.apiResponse.query.search[pos] 
) {
+                                               return 
this.apiResponse.query.search[pos].title;
+                                       } else {
+                                               return null;
+                                       }
+                               } );
+                               if ( in_ok ) {
+                                       // What exactly does this do?
+                                       // expect(found).to 
include(include(title))
+                                       throw new Error( 'Not Implemented' );
+                               } else {
+                                       expect( found ).to.include(title);
+                               }
+                       } );
+               }
+       }
+       Then( /^(.+) is( in)? the ((?:[^ ])+(?: or (?:[^ ])+)*) api search 
result$/, checkApiSearchResultStep );
+
+       function apiSearchStep( enableRewrites, qiprofile, offset, lang, 
namespaces, search ) {
+               // JSON.stringify will remove keys that have `undefined` as 
their value
+               let options = {
+                       sroffset: offset,
+                       srnamespace: (namespaces || "0").split(' '),
+                       uselang: lang,
+                       enablerewrites: enableRewrites ? 1 : 0,
+                       srqiprofile: qiprofile ? qiprofile : undefined
+               };
+               // This is reset between scenarios
+               if ( this.didyoumeanOptions ) {
+                       Object.assign(options, this.didyoumeanOptions );
+               }
+
+               // Generic string replacement of patterns stored in 
this.searchVars
+               search = Object.keys(this.searchVars).reduce( ( str, pattern ) 
=> str.replace( pattern, this.searchVars[pattern] ), search );
+               // Replace %{\uXXXX}% with the appropriate unicode code point
+               search = search.replace(/%\{\\i([\dA-Fa-f]{4,6})\}%/, ( match, 
codepoint ) => JSON.parse( `"\\u${codepoint}"` ) );
+
+               return this.stepHelpers.searchFor( search, options );
+       }
+       When(/^I api search( with rewrites enabled)?(?: with query independent 
profile ([^ ]+))?(?: with offset (\d+))?(?: in the (.*) language)?(?: in 
namespaces? (\d+(?: \d+)*))? for (.*)$/, apiSearchStep );
+
+       Then( /^within (\d+) seconds api searching for (.+) yields (.+) as the 
(.+) result$/, function( seconds, query, title, indexes ) {
+               let timeout = Date.now() + ( 1000 * seconds );
+               let runSteps = ( resolve, reject ) => {
+                       apiSearchStep.call( this, undefined, undefined, 
undefined, undefined, 0, query ).then( () => {
+                               checkApiSearchResultStep.call( this, title, 
false, indexes );
+                       } ).then( resolve, ( error ) => {
+                               if ( Date.now() > timeout ) {
+                                       console.log( 'within rejected due to 
timeout' );
+                                       reject( error );
+                               }
+                               console.log( 're-running within' );
+                               // Use process.nextTick to keep from exploding 
the stack.
+                               process.nextTick( () => runSteps( resolve, 
reject ) );
+                       } );
+               };
+               withApi( this, () => {
+                       return new Promise( runSteps );
+               } );
        } );
 
 });
diff --git a/tests/integration/features/support/hooks.js 
b/tests/integration/features/support/hooks.js
index f5ab71b..fd94196 100644
--- a/tests/integration/features/support/hooks.js
+++ b/tests/integration/features/support/hooks.js
@@ -21,6 +21,71 @@
                return true;
        } );
 
+       BeforeOnce( { tags: "@prefix" }, function () {
+               console.log( 'starting prefix hook' );
+               let batchJobs = {
+                       edit: {
+                               "L'Oréal": "L'Oréal",
+                               "Jean-Yves Le Drian": "Jean-Yves Le Drian"
+                       }
+               };
+               return this.onWiki().then( ( api ) => {
+                       return api.loginGetEditToken().then( () => {
+                               return api.batch(batchJobs, 'CirrusSearch 
integration test edit');
+                       } );
+               } );
+       } );
+
+       BeforeOnce( { tags: "@redirect" }, function () {
+               let batchJobs = {
+                       edit: {
+                               "SEO Redirecttest": "#REDIRECT [[Search Engine 
Optimization Redirecttest]]",
+                               "Redirecttest Yikes": "#REDIRECT [[Redirecttest 
Yay]]",
+                               "User_talk:SEO Redirecttest": "#REDIRECT 
[[User_talk:Search Engine Optimization Redirecttest]]",
+                               "Seo Redirecttest": "Seo Redirecttest",
+                               "Search Engine Optimization Redirecttest": 
"Search Engine Optimization Redirecttest",
+                               "Redirecttest Yay": "Redirecttest Yay",
+                               "User_talk:Search Engine Optimization 
Redirecttest": "User_talk:Search Engine Optimization Redirecttest",
+                               "PrefixRedirectRanking 1": 
"PrefixRedirectRanking 1",
+                               "LinksToPrefixRedirectRanking 1": 
"[[PrefixRedirectRanking 1]]",
+                               "TargetOfPrefixRedirectRanking 2": 
"TargetOfPrefixRedirectRanking 2",
+                               "PrefixRedirectRanking 2": "#REDIRECT 
[[TargetOfPrefixRedirectRanking 2]]"
+                       }
+               };
+               return this.onWiki().then( ( api ) => {
+                       return api.loginGetEditToken().then( () => {
+                               return api.batch(batchJobs, 'CirrusSearch 
integration test edit');
+                       } );
+               } );
+       } );
+
+       BeforeOnce( { tags: "@accent_squashing" }, function () {
+               let batchJobs = {
+                       edit: {
+                               "Áccent Sorting": "Áccent Sorting",
+                               "Accent Sorting": "Accent Sorting"
+                       }
+               };
+               return this.onWiki().then( ( api ) => {
+                       return api.loginGetEditToken().then( () => {
+                               return api.batch(batchJobs, 'CirrusSearch 
integration test edit');
+                       } );
+               } );
+       } );
+
+       BeforeOnce( { tags: "@accented_namespace" }, function () {
+               let batchJobs = {
+                       edit: {
+                               "Mó:Test": "some text"
+                       }
+               };
+               return this.onWiki().then( ( api ) => {
+                       return api.loginGetEditToken().then( () => {
+                               return api.batch(batchJobs, 'CirrusSearch 
integration test edit');
+                       } );
+               } );
+       } );
+
        BeforeOnce( { tags: "@suggest" }, function () {
                let batchJobs = {
                        edit: {
@@ -52,6 +117,10 @@
                return this.onWiki().then( ( api ) => {
                        return api.loginGetEditToken().then( () => {
                                return api.batch(batchJobs, 'CirrusSearch 
integration test edit');
+                       } ).then( () => {
+                               return api.request( {
+                                       action: 'cirrus-suggest-index'
+                               } );
                        } );
                } );
        } );
diff --git a/tests/integration/features/support/world.js 
b/tests/integration/features/support/world.js
index c7d94bb..b405433 100644
--- a/tests/integration/features/support/world.js
+++ b/tests/integration/features/support/world.js
@@ -28,7 +28,7 @@
                this.pendingResponses = {};
                this.connection.on( 'data', ( data ) => {
                        let parsed = JSON.parse( data );
-                       console.log( `received response for request 
${parsed.requestId}` );
+                       console.log( `received response for request 
${parsed.requestId}: ${data}` );
                        if ( parsed && this.pendingResponses[parsed.requestId] 
) {
                                this.pendingResponses[parsed.requestId]( parsed 
);
                                delete this.pendingResponses[parsed.requestId];
@@ -77,10 +77,16 @@
        // (I have a feeling this is prone to race conditions).
        // By suggestion of this stack overflow question.
        // 
https://stackoverflow.com/questions/26372724/pass-variables-between-step-definitions-in-cucumber-groovy
-       this.apiResponse= "";
+       this.apiResponse = "";
+       this.apiError = "";
 
        this.setApiResponse = function( value ) {
                this.apiResponse = value;
+               this.apiError = undefined;
+       };
+       this.setApiError = function ( error ) {
+               this.apiResponse = undefined;
+               this.apiError = error;
        };
 
        // Shortcut to environment configs
@@ -108,9 +114,19 @@
                                apiUrl: w.apiUrl
                        } );
                };
-               return new Promise( ( resolve ) => {
-                       resolve( client );
-               } );
+
+               // Add a generic method to get access to the request that 
triggered a response, so we
+               // can add generic error reporting that includes the requested 
api url
+               let origRawRequest = client.rawRequest;
+               client.rawRequest = function ( requestOptions ) {
+                       return origRawRequest.call( client, requestOptions 
).then( ( response ) => {
+                               response.__request = requestOptions;
+                               return response;
+                       } );
+               };
+
+               // TODO: Why a promise? I guess it's just easier to chain...
+               return Promise.resolve( client );
        };
 
        // Binding step helpers to this World.
@@ -137,6 +153,11 @@
                // logs full URL in case of typos, misplaced backslashes.
                console.log( `Visited page: ${browser.getUrl()}` );
        };
+
+       // Variables held between steps and substituted into queries
+       this.searchVars = {
+               "%ideographic_whitspace%": "\u3000"
+       };
 }
 
 defineSupportCode( function( { setWorldConstructor } ) {

-- 
To view, visit https://gerrit.wikimedia.org/r/381510
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Idbf43b536cd5185d32c4b5988f4daa0f1d6d319c
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/CirrusSearch
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to