Replace QS preview by visual preview of the edits

This commit is contained in:
Antonin Delpeuch 2018-03-20 16:20:00 +00:00
parent 2779eb97da
commit 81a18777cb
13 changed files with 356 additions and 126 deletions

View File

@ -58,6 +58,7 @@ function init() {
"scripts/warningsrenderer.js",
"scripts/langsuggest.js",
"scripts/bettersuggest.js",
"scripts/previewrenderer.js",
"scripts/dialogs/schema-alignment-dialog.js",
"scripts/dialogs/manage-account-dialog.js",
"scripts/dialogs/perform-edits-dialog.js",

View File

@ -10,9 +10,10 @@
"wikidata-schema": {
"dialog-header": "Align to Wikidata",
"dialog-explanation": "The Wikidata schema below specifies how your tabular data will be transformed into Wikidata edits. You can drag and drop the column names below in most input boxes: for each row, edits will be generated with the values in these columns.",
"preview-explanation": "This tab shows the first 10 edits that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.",
"schema-tab-header": "Schema",
"warnings-tab-header": "Issues",
"qs-preview-tab-header": "QuickStatements preview",
"edits-preview-tab-header": "Preview",
"statements-header": "Statements",
"terms-header": "Terms",
"empty-statements": "no statements added",
@ -41,7 +42,7 @@
"geoshape-with-prefix": "filename starting with \"Data:\"",
"datatype-not-supported-yet": "This datatype is not supported yet, sorry.",
"invalid-schema-warning-issues": "Your schema is incomplete, fix it to see the issues.",
"invalid-schema-warning-qs": "Your schema is incomplete, fix it to see the preview.",
"invalid-schema-warning-preview": "Your schema is incomplete, fix it to see the preview.",
"reset-button": "Reset",
"save-button": "Save",
"close-button": "Close",

View File

@ -5,7 +5,7 @@
<ul>
<li><a href="#schema-alignment-tabs-schema" bind="schemaTabHeader"></a></li>
<li><a href="#schema-alignment-tabs-warnings"><span bind="warningsTabHeader"></span> <span class="schema-alignment-total-warning-count" bind="warningsTabCount"></span></a></li>
<li><a href="#schema-alignment-tabs-preview-qs" bind="qsPreviewTabHeader"></a></li>
<li><a href="#schema-alignment-tabs-preview-edits" bind="editsPreviewTabHeader"></a></li>
</ul>
<div id="schema-alignment-tabs-schema">
<p class="body-text" bind="dialogExplanation"></p>
@ -23,8 +23,9 @@
<div class="invalid-schema-warning" bind="invalidSchemaWarningIssues"></div>
<div class="schema-alignment-dialog-warnings" bind="warningsArea"></div>
</div>
<div id="schema-alignment-tabs-preview-qs" style="display: none;">
<div class="invalid-schema-warning" bind="invalidSchemaWarningQs"></div>
<div id="schema-alignment-tabs-preview-edits" style="display: none;">
<p class="body-text" bind="previewExplanation"></p>
<div class="invalid-schema-warning" bind="invalidSchemaWarningPreview"></div>
<div class="schema-alignment-dialog-preview"></div>
</div>
</div>

View File

@ -115,12 +115,13 @@ SchemaAlignmentDialog._createDialog = function() {
this._elmts.dialogHeader.text($.i18n._('wikidata-schema')["dialog-header"]);
this._elmts.dialogExplanation.text($.i18n._('wikidata-schema')["dialog-explanation"]);
this._elmts.previewExplanation.text($.i18n._('wikidata-schema')["preview-explanation"]);
this._elmts.schemaTabHeader.text($.i18n._('wikidata-schema')["schema-tab-header"]);
this._elmts.warningsTabHeader.text($.i18n._('wikidata-schema')["warnings-tab-header"]);
this._elmts.qsPreviewTabHeader.text($.i18n._('wikidata-schema')["qs-preview-tab-header"]);
this._elmts.editsPreviewTabHeader.text($.i18n._('wikidata-schema')["edits-preview-tab-header"]);
SchemaAlignmentDialog._plusButton($.i18n._('wikidata-schema')["add-item-button"], this._elmts.addItemButton);
this._elmts.invalidSchemaWarningIssues.text($.i18n._('wikidata-schema')["invalid-schema-warning-issues"]);
this._elmts.invalidSchemaWarningQs.text($.i18n._('wikidata-schema')["invalid-schema-warning-qs"]);
this._elmts.invalidSchemaWarningPreview.text($.i18n._('wikidata-schema')["invalid-schema-warning-preview"]);
this._elmts.resetButton.text($.i18n._('wikidata-schema')["reset-button"]);
this._elmts.saveButton.text($.i18n._('wikidata-schema')["save-button"]);
this._elmts.closeButton.text($.i18n._('wikidata-schema')["close-button"]);
@ -939,7 +940,7 @@ SchemaAlignmentDialog._removeStatement = function(statement) {
SchemaAlignmentDialog.getJSON = function() {
var list = new Array();
$('.wbs-item').each(function () {
$('#schema-alignment-statements-container .wbs-item').each(function () {
list.push(SchemaAlignmentDialog._itemToJSON($(this)));
});
return {
@ -963,8 +964,9 @@ SchemaAlignmentDialog.preview = function(initial) {
"command/wikidata/preview-wikibase-schema?" + $.param({ project: theProject.id }),
{ schema: JSON.stringify(schema), engine: JSON.stringify(ui.browsingEngine.getJSON()) },
function(data) {
if ("quickstatements" in data) {
$(self._previewPanes[0]).text(data.quickstatements);
if ("edits_preview" in data) {
var previewContainer = self._previewPanes[0];
EditRenderer.renderEdits(data.edits_preview, previewContainer);
}
if (data.warnings) {

View File

@ -0,0 +1,276 @@
/**
* renders an item update (an edit on an item) in HTML.
*/
var EditRenderer = {};
// main method: takes a DOM element and a list
// of edits to render there.
EditRenderer.renderEdits = function(edits, container) {
for(var i = 0; i != edits.length; i++) {
EditRenderer._renderItem(edits[i], container);
}
}
/**************/
/*** ITEMS ****/
/**************/
EditRenderer._renderItem = function(json, container) {
var subject = json;
var statementGroups = null;
var nameDescs = null;
if (json) {
subject = json.subject;
statementGroups = json.statementGroups;
nameDescs = json.nameDescs;
}
var item = $('<div></div>').addClass('wbs-item').appendTo(container);
var inputContainer = $('<div></div>').addClass('wbs-item-input').appendTo(item);
EditRenderer._renderEntity(json.subject, inputContainer);
var right = $('<div></div>').addClass('wbs-item-contents').appendTo(item);
// Terms
if ((json.labels && json.labels.length) ||
(json.descriptions && json.descriptions.length) ||
(json.addedAliases && json.addedAliases.length)) {
//$('<span></span>').addClass('wbs-namedesc-header')
// .text($.i18n._('wikidata-schema')["terms-header"]).appendTo(right);
var termsContainer = $('<div></div>').addClass('wbs-namedesc-container')
.appendTo(right);
for(var i = 0; i != json.labels.length; i++) {
EditRenderer._renderTerm("label", json.labels[i], termsContainer);
}
for(var i = 0; i != json.descriptions.length; i++) {
EditRenderer._renderTerm("description", json.descriptions[i], termsContainer);
}
for(var i = 0; i != json.addedAliases.length; i++) {
EditRenderer._renderTerm("alias", json.addedAliases[i], termsContainer);
}
// Clear the float
$('<div></div>').attr('style', 'clear: right').appendTo(right);
}
// Statements
if (json.addedStatementGroups && json.addedStatementGroups.length) {
// $('<div></div>').addClass('wbs-statements-header')
// .text($.i18n._('wikidata-schema')['statements-header']).appendTo(right);
var statementsGroupContainer = $('<div></div>').addClass('wbs-statement-group-container')
.appendTo(right);
for(var i = 0; i != json.addedStatementGroups.length; i++) {
EditRenderer._renderStatementGroup(json.addedStatementGroups[i], statementsGroupContainer);
}
}
}
/**************************
* NAMES AND DESCRIPTIONS *
**************************/
EditRenderer._renderTerm = function(termType, json, container) {
var namedesc = $('<div></div>').addClass('wbs-namedesc').appendTo(container);
var type_container = $('<div></div>').addClass('wbs-namedesc-type').appendTo(namedesc);
var type_span = $('<span></span>').appendTo(type_container)
.text($.i18n._('wikidata-schema')[termType]);
var right = $('<div></div>').addClass('wbs-right').appendTo(namedesc);
var value_container = $('<div></div>').addClass('wbs-namedesc-value').appendTo(namedesc);
EditRenderer._renderValue({datavalue:json,datatype:'monolingualtext'}, value_container);
}
/********************
* STATEMENT GROUPS *
********************/
EditRenderer._renderStatementGroup = function(json, container) {
var statementGroup = $('<div></div>').addClass('wbs-statement-group').appendTo(container);
var inputContainer = $('<div></div>').addClass('wbs-prop-input').appendTo(statementGroup);
var right = $('<div></div>').addClass('wbs-right').appendTo(statementGroup);
EditRenderer._renderEntity(json.property, inputContainer);
var statementContainer = $('<div></div>').addClass('wbs-statement-container').appendTo(right);
for (var i = 0; i != json.statements.length; i++) {
EditRenderer._renderStatement(json.statements[i], statementContainer);
}
}
/**************
* STATEMENTS *
**************/
EditRenderer._renderStatement = function(json, container) {
var statement = $('<div></div>').addClass('wbs-statement').appendTo(container);
var inputContainer = $('<div></div>').addClass('wbs-target-input').appendTo(statement);
EditRenderer._renderValue(json.mainsnak, inputContainer);
// add rank
var rank = $('<div></div>').addClass('wbs-rank-selector-icon').prependTo(inputContainer);
// add qualifiers...
var right = $('<div></div>').addClass('wbs-right').appendTo(statement);
var qualifierContainer = $('<div></div>').addClass('wbs-qualifier-container').appendTo(right);
if (json.qualifiers) {
for (var pid in json.qualifiers) {
if (json.qualifiers.hasOwnProperty(pid)) {
var qualifiers = json.qualifiers[pid];
for (var i = 0; i != qualifiers.length; i++) {
EditRenderer._renderSnak(qualifiers[i], qualifierContainer);
}
}
}
}
// and references
$('<div></div>').attr('style', 'clear: right').appendTo(statement);
var referencesToggleContainer = $('<div></div>').addClass('wbs-references-toggle').appendTo(statement);
var triangle = $('<div></div>').addClass('triangle-icon').addClass('pointing-right').appendTo(referencesToggleContainer);
var referencesToggle = $('<a></a>').appendTo(referencesToggleContainer);
right = $('<div></div>').addClass('wbs-right').appendTo(statement);
var referenceContainer = $('<div></div>').addClass('wbs-reference-container').appendTo(right);
referencesToggle.click(function () {
triangle.toggleClass('pointing-down');
triangle.toggleClass('pointing-right');
referenceContainer.toggle(100);
});
referenceContainer.hide();
if (json.references) {
for (var i = 0; i != json.references.length; i++) {
EditRenderer._renderReference(json.references[i], referenceContainer);
}
}
EditRenderer._updateReferencesNumber(referenceContainer);
}
/*********************************
* QUALIFIER AND REFERENCE SNAKS *
*********************************/
EditRenderer._renderSnak = function(json, container) {
var qualifier = $('<div></div>').addClass('wbs-qualifier').appendTo(container);
var toolbar1 = $('<div></div>').addClass('wbs-toolbar').appendTo(qualifier);
var inputContainer = $('<div></div>').addClass('wbs-prop-input').appendTo(qualifier);
var right = $('<div></div>').addClass('wbs-right').appendTo(qualifier);
var statementContainer = $('<div></div>').addClass('wbs-statement-container').appendTo(right);
EditRenderer._renderEntity(json.full_property, inputContainer);
EditRenderer._renderValue(json, statementContainer);
}
/**************
* REFERENCES *
**************/
EditRenderer._renderReference = function(json, container) {
var reference = $('<div></div>').addClass('wbs-reference').appendTo(container);
var referenceHeader = $('<div></div>').addClass('wbs-reference-header').appendTo(reference);
var right = $('<div></div>').addClass('wbs-right').appendTo(reference);
var qualifierContainer = $('<div></div>').addClass('wbs-qualifier-container').appendTo(right);
for (var pid in json.snaks) {
if (json.snaks.hasOwnProperty(pid)) {
var snaks = json.snaks[pid];
for(var i = 0; i != snaks.length; i++) {
EditRenderer._renderSnak(snaks[i], qualifierContainer);
}
}
}
}
EditRenderer._updateReferencesNumber = function(container) {
var childrenCount = container.children().length;
var statement = container.parents('.wbs-statement');
var a = statement.find('.wbs-references-toggle a').first();
a.html(childrenCount+$.i18n._('wikidata-schema')["nb-references"]);
}
/*******************
* VALUE RENDERING *
*******************/
EditRenderer._renderEntity = function(json, container) {
var html = WarningsRenderer._renderEntity(json);
$(html).appendTo(container);
}
EditRenderer._renderValue = function(json, container) {
var input = $('<span></span>').appendTo(container);
var mode = json.datatype;
if (mode === "wikibase-item" || mode === "wikibase-property") {
EditRenderer._renderEntity(json.datavalue, container);
} else {
var params = {
action: 'wbformatvalue',
generate: 'text/html',
datavalue: JSON.stringify(json.datavalue),
options: '{"lang":"'+$.i18n._('core-recon')["wd-recon-lang"]+'"}',
format: 'json'
};
if ('property' in json) {
params.property = json.property;
} else {
params.datatype = json.datatype;
}
$.get(
'https://www.wikidata.org/w/api.php',
params,
function (data) {
if('result' in data) {
$('<span>'+data.result+'</span>').appendTo(container);
}
},
'jsonp'
);
}
/*
if (mode === "time") {
input.text(json.datavalue.value.time);
} else if (mode === "globe-coordinate") {
// TODO
} else if (mode === "language") {
// TODO
} else if (mode === "monolingualtext") {
input.remove();
var inputContainerLanguage = $('<div></div>')
.addClass('wbs-monolingual-container')
.width('30%')
.appendTo(container);
var inputContainerValue = $('<div></div>')
.addClass('wbs-monolingual-container')
.width('70%')
.appendTo(container);
EditRenderer._renderValue({datatype:"language",datavalue:json.language}, inputContainerLanguage);
EditRenderer._renderValue({datatype:"string",datavalue:{value:json.value}}, inputContainerValue);
} else if (mode === "quantity") {
input.remove();
var inputContainerAmount = $('<div></div>')
.addClass('wbs-quantity-container')
.width('60%')
.appendTo(inputContainer);
var inputContainerUnit = $('<div></div>')
.addClass('wbs-quantity-container')
.width('40%')
.appendTo(inputContainer);
var amountValue = null;
var unitValue = null;
// TODO
//EditRenderer._renderValue(inputContainerAmount, "amount", amountValue, propagateValue);
// EditRenderer._renderEntity( , inputContainerUnit);
} else {
input.text(json.datavalue.value);
} */
}

View File

@ -204,6 +204,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
color: gray;
}
.schema-alignment-dialog-preview .wbs-qualifier .wbs-statement-container {
display: table-cell;
vertical-align: bottom;
padding-left: 10px;
height: 20px;
}
.wbs-statement-group-container, .wbs-statement-container, .wbs-qualifier-container, .wbs-reference-container {
width: 100%;
display: block;
@ -228,6 +235,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.wbs-statement {
background: white;
border-bottom: 1px solid #eaecf0;
}
.wbs-statement:last-child {
border-bottom: 0;
}
.wbs-right {
@ -236,7 +248,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
.wbs-statement-group .wbs-right {
width: 70%;
width: 75%;
}
.wbs-statement .wbs-right {
@ -450,6 +462,5 @@ div.schema-alignment-dialog-preview {
padding: 10px;
margin-top: 3px;
white-space: pre;
font-family: monospace;
font-size: 9pt;
}

View File

@ -28,22 +28,27 @@ import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONWriter;
import org.openrefine.wikidata.exporters.QuickStatementsExporter;
import org.openrefine.wikidata.qa.EditInspector;
import org.openrefine.wikidata.qa.QAWarning;
import org.openrefine.wikidata.qa.QAWarningStore;
import org.openrefine.wikidata.schema.WikibaseSchema;
import org.openrefine.wikidata.updates.ItemUpdate;
import org.openrefine.wikidata.utils.FirstLinesExtractor;
import org.openrefine.wikidata.updates.scheduler.WikibaseAPIUpdateScheduler;
import static org.openrefine.wikidata.commands.CommandUtilities.respondError;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.refine.browsing.Engine;
import com.google.refine.commands.Command;
import com.google.refine.model.Project;
@ -110,12 +115,17 @@ public class PreviewWikibaseSchemaCommand extends Command {
writer.key("nb_warnings");
writer.value(warningStore.getNbWarnings());
// Export to QuickStatements
QuickStatementsExporter exporter = new QuickStatementsExporter();
exporter.translateItemList(editBatch, stringWriter);
// Dump the first 10 edits, scheduled with the default scheduler
WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler();
List<ItemUpdate> firstEdits = scheduler.schedule(editBatch).stream()
.filter(e -> !e.isNull())
.limit(10)
.collect(Collectors.toList());
ObjectMapper mapper = new ObjectMapper();
String firstEditsJson = mapper.writeValueAsString(firstEdits);
writer.key("quickstatements");
writer.value(FirstLinesExtractor.extractFirstLines(stringWriter.toString(), 50));
writer.key("edits_preview");
writer.value(new JSONArray(firstEditsJson));
}
writer.endObject();

View File

@ -24,6 +24,7 @@
package org.openrefine.wikidata.schema;
import org.jsoup.helper.Validate;
import org.openrefine.wikidata.schema.entityvalues.FullyPropertySerializingValueSnak;
import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException;
import org.wikidata.wdtk.datamodel.helpers.Datamodel;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
@ -62,7 +63,7 @@ public class WbSnakExpr implements WbExpression<Snak> {
throws SkipSchemaExpressionException {
PropertyIdValue propertyId = getProp().evaluate(ctxt);
Value evaluatedValue = value.evaluate(ctxt);
return Datamodel.makeValueSnak(propertyId, evaluatedValue);
return new FullyPropertySerializingValueSnak(propertyId, evaluatedValue);
}
@JsonProperty("prop")

View File

@ -0,0 +1,28 @@
package org.openrefine.wikidata.schema.entityvalues;
import org.wikidata.wdtk.datamodel.implementation.ValueSnakImpl;
import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
import org.wikidata.wdtk.datamodel.interfaces.Value;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A tweaked version of {@link SnakImpl} that serializes
* the full property (not just its PID), so that we can also
* get the label for that property and display it in the UI
* without having to query the remove server.
*
* @author Antonin Delpeuch
*
*/
public class FullyPropertySerializingValueSnak extends ValueSnakImpl {
public FullyPropertySerializingValueSnak(PropertyIdValue property, Value value) {
super(property, value);
}
@JsonProperty("full_property")
public PropertyIdValue getFullPropertyId() {
return getPropertyId();
}
}

View File

@ -105,6 +105,7 @@ public abstract class ReconEntityIdValue implements PrefetchedEntityIdValue {
*
* @return the full reconciliation metadata of the corresponding cell
*/
@JsonIgnore // just to clean up a bit the json serialization
public Recon getRecon() {
return _recon;
}

View File

@ -1,60 +0,0 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.utils;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.io.StringWriter;
public class FirstLinesExtractor {
/**
* Returns the first n lines of a given string
*
* @param content
* the content, where lines are separated by '\n'
* @param nbLines
* the number of lines to extract
* @return the first lines of the string
* @throws IOException
*/
public static String extractFirstLines(String content, int nbLines)
throws IOException {
StringWriter stringWriter = new StringWriter();
LineNumberReader reader = new LineNumberReader(new StringReader(content));
// Only keep the first 50 lines
reader.setLineNumber(0);
String line = reader.readLine();
for (int i = 1; i != nbLines && line != null; i++) {
stringWriter.write(line + "\n");
line = reader.readLine();
}
if (reader.getLineNumber() == nbLines) {
stringWriter.write("...");
}
return stringWriter.toString();
}
}

View File

@ -31,8 +31,10 @@ import java.io.IOException;
import javax.servlet.ServletException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Assert;
import org.openrefine.wikidata.testing.TestingData;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -56,7 +58,8 @@ public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest {
command.doPost(request, response);
JSONObject response = ParsingUtilities.evaluateJsonStringToObject(writer.toString());
assertEquals(TestingData.inceptionWithNewQS, response.getString("quickstatements"));
JSONArray edits = response.getJSONArray("edits_preview");
assertEquals(4, edits.length());
}
}

View File

@ -1,45 +0,0 @@
/*******************************************************************************
* MIT License
*
* Copyright (c) 2018 Antonin Delpeuch
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
******************************************************************************/
package org.openrefine.wikidata.utils;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.testng.annotations.Test;
public class FirstLinesExtractorTest {
@Test
public void testShort()
throws IOException {
assertEquals("a\nb\nc\n", FirstLinesExtractor.extractFirstLines("a\nb\nc\n", 5));
}
@Test
public void testLong()
throws IOException {
assertEquals("a\nb\n...", FirstLinesExtractor.extractFirstLines("a\nb\nc", 3));
}
}