Faits :
A.
Au début 2008, A._ a souhaité changer le mobilier du restaurant qu'elle exploitait à X._. En août 2008, l'époux de B._ est venu chercher ces objets; un inventaire détaille les pièces et indique un prix total de 22'400 fr.
Par la suite, B._ et son époux ont utilisé une partie du mobilier dans l'établissement qu'ils ont repris en mars 2009.
Le 24 octobre 2011, A._ a introduit une poursuite contre B._ pour le montant de 22'400 fr. représentant le prix de vente du mobilier emporté; la poursuivie a formé opposition.
B.
A._ a ouvert action contre B._ en paiement de 22'400 fr.
Le Tribunal de première instance du canton de Genève a entendu divers témoins. Par jugement du 26 mai 2014, il a condamné B._ à payer 6'000 fr. pour les pièces qu'elle utilisait dans son restaurant; il a rejeté la demande pour le surplus.
Statuant le 27 mars 2015 sur appel de A._, la Chambre civile de la Cour de justice du canton de Genève a confirmé le jugement de première instance. Son raisonnement peut être résumé comme suit.
A._ soutenait que les parties avaient conclu en août 2008 un contrat portant sur la vente de l'ensemble des objets figurant sur l'inventaire pour un prix de 22'400 fr., payable immédiatement ou plus tard. Pour sa part, B._ alléguait avoir pris le mobilier en dépôt, le temps que A._ trouve un acquéreur; elle aurait par la suite proposé d'acheter une partie du mobilier, mais A._ aurait refusé. La Chambre civile a apprécié les preuves administrées de manière détaillée. Elle a jugé que A._ avait échoué à apporter la preuve de l'existence d'un contrat de vente conclu en août 2008, même si la thèse du dépôt défendue par B._ n'était pas non plus convaincante. Les éléments du dossier ne démontraient ainsi pas ce que les parties avaient réellement voulu et convenu en 2008. La cour cantonale a ajouté qu'une interprétation objective fondée sur le principe de la confiance ne permettait pas non plus de retenir la conclusion d'un contrat de vente. Selon l'arrêt cantonal, A._ ne peut dès lors prétendre au paiement d'un prix plus élevé que celui de 6'000 fr. arrêté par le premier juge et non contesté en appel par B._.
C.
A._ interjette un recours constitutionnel subsidiaire, concluant à ce que B._ soit condamnée à lui payer 22'400 fr. avec intérêts à 5% depuis le 1 er août 2008.
B._ propose le rejet du recours dans la mesure où il est recevable.

Considérant en droit :
1.
1.1. Le recours est dirigé contre une décision rendue en matière civile (<ref-law>). Devant l'autorité précédente, les conclusions restées litigieuses portaient sur 16'400 fr. (<ref-law>). La valeur litigieuse n'atteint donc pas le minimum légal de 30'000 fr. prévu par l'<ref-law> pour la recevabilité du recours en matière civile dans les cas ne relevant ni du droit du travail ni du droit du bail à loyer. Comme aucun des cas de dispense (<ref-law>) n'est réalisé, seul le recours constitutionnel subsidiaire (<ref-law>) est ouvert en l'espèce.
1.2. L'arrêt attaqué est une décision finale (art. 117 et 90 al. 1 LTF), prononcée sur recours par le tribunal supérieur du canton concerné (art. 114 et 75 LTF). La recourante a pris part à l'instance précédente et succombé partiellement dans ses conclusions condamnatoires; elle a ainsi qualité pour recourir (<ref-law>).
Au surplus, le recours a été déposé en temps utile (art. 117, art. 46 al. 1 let. a et art 100 al. 1 LTF) et dans la forme requise (<ref-law>).
2.
Le recours se limite à une critique de l'appréciation des preuves par la cour cantonale.
2.1. Le recours constitutionnel peut être formé pour violation des droits constitutionnels (<ref-law>). Le Tribunal fédéral statue sur la base des faits constatés par l'autorité précédente (<ref-law>). Il ne peut s'en écarter que si les faits ont été établis en violation du droit au sens de l'<ref-law> (<ref-law>), en particulier s'ils résultent d'une appréciation arbitraire des preuves (art. 9 Cst.). Le Tribunal fédéral n'examine la violation de droits constitutionnels que si ce grief a été invoqué et motivé de manière circonstanciée. En particulier, il n'entre pas en matière sur des critiques de type appellatoire portant sur l'état de fait ou sur l'appréciation des preuves (art. 117 et 106 al. 2 LTF; cf. <ref-ruling> consid. 1.3.1 p. 18; <ref-ruling> consid. 4.1.2 p. 62; <ref-ruling> consid. 5.1 p. 356; <ref-ruling> consid. 1.4.1 p. 53; <ref-ruling> consid. 2.8 p. 494; <ref-ruling> consid. 2.2 p. 246; <ref-ruling> consid. 1.4.3 p. 255).
Une décision est arbitraire lorsqu'elle viole gravement une norme ou un principe juridique clair et indiscuté, ou contredit d'une manière choquante le sentiment de la justice et de l'équité. L'arbitraire, prohibé par l'art. 9 Cst., ne résulte pas du seul fait qu'une autre solution pourrait entrer en considération ou même qu'elle serait préférable. Le Tribunal fédéral ne s'écarte de la solution retenue par l'autorité cantonale de dernière instance que si sa décision apparaît insoutenable, en contradiction manifeste avec la situation effective, adoptée sans motifs objectifs ou en violation d'un droit certain. Il ne suffit pas que les motifs de la décision soient insoutenables; encore faut-il que celle-ci soit arbitraire dans son résultat (<ref-ruling> consid. 2.1 p. 18 s., 157 consid. 2.1 p. 168; <ref-ruling> consid. 3.2.5 p. 339; <ref-ruling> consid. 6.1 p. 379 s.).
En matière d'appréciation des preuves, il y a arbitraire lorsque l'autorité ne prend pas en compte, sans aucune raison sérieuse, un élément de preuve propre à modifier la décision, lorsqu'elle se trompe manifestement sur son sens et sa portée, ou encore lorsque, en se fondant sur les éléments recueillis, elle en tire des constatations insoutenables (ATF <ref-ruling> consid. 4.1.2 p. 62; <ref-ruling> consid. 4.2 p. 234; <ref-ruling> consid. 4.2 p. 560; <ref-ruling> consid. 4.3 p. 62; <ref-ruling> consid. 2.1 p. 9).
3.
3.1. La recourante commence par relever deux faits, tirés de témoignages, que l'autorité précédente n'aurait arbitrairement pas retenus. Ces faits constitueraient des éléments supplémentaires tendant à prouver la conclusion d'un contrat de vente portant sur l'ensemble du mobilier emporté par l'intimée. Or, un indice supplémentaire n'est pas propre à modifier le sort de la cause, en faisant apparaître comme arbitraires les doutes de l'instance précédente au sujet des intentions réelles des parties. Il n'y a donc pas à entrer en matière.
Au demeurant, on ne discerne pas la pertinence des faits relevés.
Premièrement, la recourante invoque le fait que les parties se seraient assises autour d'une table pour contrôler l'inventaire. Contrairement à l'avis de la recourante, il ne s'agit là nullement d'un indice supplémentaire en faveur de la thèse de la vente; la vérification prenait tout son sens également en cas de contrat de dépôt.
Le second fait porte sur le défaut d'aide de la part de la recourante lors du déménagement du matériel. Cette absence de participation tendrait à renforcer les indices propres à démontrer que l'intimée avait bien la volonté d'acquérir tout le mobilier au prix figurant sur l'inventaire, car si l'intimée avait seulement pris le matériel en dépôt, la recourante aurait en tout cas apporté son aide lors du déménagement en août 2008. Tirée du comportement de la recourante, cette déduction à propos de l'intention de l'intimée est pour le moins sujette à caution.
3.2. La recourante énumère ensuite "les éléments du dossier permettant de prouver la conclusion du contrat de vente, soit la volonté réciproque des parties et leur entente sur le prix de vente". La démonstration consiste en une argumentation appellatoire typique, dans laquelle l'intéressée expose comment les éléments du dossier doivent, à son avis, être interprétés. Ce faisant, la recourante n'explique pas en quoi l'appréciation différente de l'autorité précédente est arbitraire; l'utilisation répétée du terme "arbitraire" n'y change rien. Une telle argumentation n'est pas recevable. Au demeurant, sur le vu des éléments relevés dans l'arrêt attaqué, il n'était manifestement pas insoutenable de la part de la cour cantonale d'éprouver des doutes sur l'intention réelle commune des parties en août 2008.
4.
Faute de grief recevable, le recours se révèle irrecevable.
La recourante, qui succombe, prendra à sa charge les frais judiciaires (<ref-law>) et versera des dépens à l'intimée (art. 68 al. 1 et 2 LTF).

Par ces motifs, le Tribunal fédéral prononce :
1.
Le recours est irrecevable.
2.
Les frais judiciaires, arrêtés à 2'000 fr., sont mis à la charge de la recourante.
3.
La recourante versera à l'intimée une indemnité de 2'500 fr. à titre de dépens.
4.
Le présent arrêt est communiqué aux mandataires des parties et à la Chambre civile de la Cour de justice du canton de Genève.
Lausanne, le 5 août 2015
Au nom de la Ire Cour de droit civil
du Tribunal fédéral suisse
La Présidente : Kiss
La Greffière : Godat Zimmermann